From cfceb087b6f196290437d8eac02d22786774266f Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Dec 21 2017 05:06:13 +0000 Subject: assistance: add missing files --- diff --git a/mono/Assistance/AssistantGrid.cs b/mono/Assistance/AssistantGrid.cs new file mode 100644 index 0000000..7e14c6e --- /dev/null +++ b/mono/Assistance/AssistantGrid.cs @@ -0,0 +1,22 @@ +using System; + +namespace Assistance { + public class AssistantGrid: Assistant { + public ActivePoint center; + + public AssistantGrid(Workarea canvas, Point center): base(canvas) { + this.center = new ActivePoint(this, ActivePoint.Type.CircleCross, center); + } + + public override void draw(System.Drawing.Graphics g) { + /* + foreach(Assistant assistant in canvas.assistants) + foreach(Point p in assistant.getGridPoints(center.position)) + foreach(Assistant a in canvas.assistants) + if (a != assistant) + a.drawGuidlines(g, p); + */ + } + } +} + diff --git a/mono/Assistance/AssistantVanishingPoint.cs b/mono/Assistance/AssistantVanishingPoint.cs new file mode 100644 index 0000000..7786dda --- /dev/null +++ b/mono/Assistance/AssistantVanishingPoint.cs @@ -0,0 +1,123 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; + +namespace Assistance { + public class AssistantVanishingPoint: Assistant { + public ActivePoint center; + public ActivePoint a0; + public ActivePoint a1; + public ActivePoint b0; + public ActivePoint b1; + public ActivePoint step; + + public AssistantVanishingPoint(Workarea canvas, Point center): base(canvas) { + this.center = new ActivePoint(this, ActivePoint.Type.CircleCross, center); + a0 = new ActivePoint(this, ActivePoint.Type.CircleFill, center + new Point(-100.0, 0.0)); + a1 = new ActivePoint(this, ActivePoint.Type.Circle, center + new Point(-200.0, 0.0)); + b0 = new ActivePoint(this, ActivePoint.Type.CircleFill, center + new Point(100.0, 0.0)); + b1 = new ActivePoint(this, ActivePoint.Type.Circle, center + new Point(200.0, 0.0)); + step = new ActivePoint(this, ActivePoint.Type.Circle, (a0.position + a1.position)/2); + } + + private void fixCenter() { + if (!a0.position.isEqual(a1.position) && !b0.position.isEqual(b1.position)) { + Point a = a0.position; + Point b = b0.position; + Point da = a1.position - a; + Point db = b1.position - b; + Point ab = b - a; + double k = db.x*da.y - db.y*da.x; + if (Math.Abs(k) > 0.00001) { + double lb = (da.x*ab.y - da.y*ab.x)/k; + center.position.x = lb*db.x + b.x; + center.position.y = lb*db.y + b.y; + } + } + } + + private void fixSidePoint(ActivePoint p0, ActivePoint p1, Point previousP0) { + if (!p0.position.isEqual(center.position)) { + Point dp0 = p0.position - center.position; + Point dp1 = p1.position - previousP0; + p1.position = center.position + dp0*(dp0.len() + dp1.len())/dp0.len(); + } + } + + private void fixSidePoint(ActivePoint p0, ActivePoint p1) { + fixSidePoint(p0, p1, p0.position); + } + + public void fixPoints() { + fixSidePoint(a0, a1); + fixSidePoint(a0, step); + fixSidePoint(b0, b1); + fixCenter(); + } + + public override void onMovePoint(ActivePoint point, Point position) { + Point previous = point.position; + point.position = position; + if (point == center) { + a0.position += point.position - previous; + a1.position += point.position - previous; + step.position += point.position - previous; + b0.position += point.position - previous; + b1.position += point.position - previous; + } else + if (point == a0) { + fixSidePoint(a0, a1, previous); + fixSidePoint(a0, step, previous); + fixSidePoint(b0, b1); + } else + if (point == b0) { + fixSidePoint(a0, a1); + fixSidePoint(a0, step); + fixSidePoint(b0, b1, previous); + } else { + fixCenter(); + fixSidePoint(a0, a1); + fixSidePoint(a0, step); + fixSidePoint(b0, b1); + } + } + + /* + public override Point[] getGridPoints(Point target, bool truncate) { + double k = (a0.position - center.position).len(); + if (k < 0.00001) + return new Point[0]; + k = (step.position - center.position).len()/k; + + //if (Math.Abs(k - 1.0) < 0.1) return new Point[0]; + + Point[] points = new Point[truncate ? gridPointsCount + 1 : gridPointsCount*2 + 1]; + Point a = target - center.position; + Point b = a; + points[gridPointsCount] = a + center.position; + for(int i = 1; i <= gridPointsCount; ++i) { + a /= k; + b *= k; + points[gridPointsCount - i] = a + center.position; + if (!truncate) + points[gridPointsCount + i] = b + center.position; + } + return points; + } + */ + + public override void draw(System.Drawing.Graphics g) { + g.DrawLine(pen, center.position.toFloat(), a0.position.toFloat()); + g.DrawLine(pen, center.position.toFloat(), a1.position.toFloat()); + g.DrawLine(pen, center.position.toFloat(), b0.position.toFloat()); + g.DrawLine(pen, center.position.toFloat(), b1.position.toFloat()); + } + + public override void getGuidelines(List outGuidelines, Point target) { + if (!target.isEqual(center.position)) + outGuidelines.Add(new GuidelineLine(target, center.position)); + } + } +} + diff --git a/mono/Assistance/Geometry.cs b/mono/Assistance/Geometry.cs new file mode 100644 index 0000000..2c7835b --- /dev/null +++ b/mono/Assistance/Geometry.cs @@ -0,0 +1,32 @@ +using System; + +namespace Assistance { + public static class Geometry { + public static readonly double precision = 0.01; + public static readonly double sqrt2Pi = Math.Sqrt(2.0*Math.PI); + + public static double logNormalDistribuitionUnscaled(double x, double x0, double w) { + return Math.Exp(-0.5*Math.Pow(Math.Log(x/x0)/w, 2.0))/x; + } + + public static double logNormalDistribuition(double x, double x0, double w) { + return logNormalDistribuition(x, x0, w)/(w*sqrt2Pi); + } + + public static void truncateInfiniteLine(Rectangle bounds, ref Point p0, ref Point p1) { + if (p0.isEqual(p1)) return; + Point d = p0 - p1; + if (Math.Abs(d.x)*bounds.height > bounds.width*Math.Abs(d.y)) { + // horizontal + double k = d.y/d.x; + p1 = new Point(bounds.x1, p0.y + k*(bounds.x1 - p0.x)); + p0 = new Point(bounds.x0, p0.y + k*(bounds.x0 - p0.x)); + } else { + // vertical + double k = d.x/d.y; + p1 = new Point(p0.x + k*(bounds.y1 - p0.y), bounds.y1); + p0 = new Point(p0.x + k*(bounds.y0 - p0.y), bounds.y0); + } + } + } +} diff --git a/mono/Assistance/Guideline.cs b/mono/Assistance/Guideline.cs new file mode 100644 index 0000000..2695fa3 --- /dev/null +++ b/mono/Assistance/Guideline.cs @@ -0,0 +1,66 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; + +namespace Assistance { + public class Guideline { + public static readonly Pen pen = Pens.LightGray; + public static readonly Pen penActive = Pens.DeepSkyBlue; + public static readonly double snapLenght = 20.0; + public static readonly double snapScale = 1.0; + + public virtual Point transformPoint(Point p) { + return p; + } + + public virtual void draw(Graphics g, bool active) { } + + public void draw(Graphics g) { + draw(g, false); + } + + public double calcTrackWeight(Track track) { + if (track.points.Count < 2) + return double.PositiveInfinity; + double sumWeight = 0.0; + double sumLength = 0.0; + double sumDeviation = 0.0; + + for(int i = 1; i < track.points.Count; ++i) { + Point point = track.points[i]; + + double length = (point - track.points[i - 1]).len(); + sumLength += length; + + double weight = Geometry.logNormalDistribuitionUnscaled(sumLength, snapLenght, snapScale); + sumWeight += weight; + + double deviation = (transformPoint(point) - point).len(); + sumDeviation += weight*deviation; + } + return sumDeviation/sumWeight; + } + + public Track modifyTrack(Track track) { + Track t = new Track(); + foreach(Point p in track.points) + t.points.Add( transformPoint(p) ); + return t; + } + + public static Guideline findBest(List guidelines, Track track) { + double bestWeight = double.PositiveInfinity; + Guideline best = null; + foreach(Guideline guideline in guidelines) { + double weight = guideline.calcTrackWeight(track); + if (weight < bestWeight) { + bestWeight = weight; + best = guideline; + } + } + return best; + } + } +} + diff --git a/mono/Assistance/GuidelineLine.cs b/mono/Assistance/GuidelineLine.cs new file mode 100644 index 0000000..8d49133 --- /dev/null +++ b/mono/Assistance/GuidelineLine.cs @@ -0,0 +1,29 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; + +namespace Assistance { + public class GuidelineLine: Guideline { + protected Point p0, p1; + protected Point direction; + + public GuidelineLine(Point p0, Point p1) { + this.p0 = p0; + this.p1 = p1; + direction = (p1 - p0).normalize(); + } + + public override void draw(Graphics g, bool active) { + Point pp0 = p0; + Point pp1 = p1; + Geometry.truncateInfiniteLine(new Rectangle(g.VisibleClipBounds), ref pp0, ref pp1); + g.DrawLine(active ? penActive : pen , pp0.toFloat(), pp1.toFloat()); + } + + public override Point transformPoint(Point p) { + return Point.dot(p - p0, direction)*direction + p0; + } + } +} + diff --git a/mono/Assistance/Rectangle.cs b/mono/Assistance/Rectangle.cs new file mode 100644 index 0000000..68324e8 --- /dev/null +++ b/mono/Assistance/Rectangle.cs @@ -0,0 +1,106 @@ +using System; + +namespace Assistance { + public struct Rectangle { + public double x0, y0, x1, y1; + + public Point p0 { + get { return new Point(x0, y0); } + set { x0 = value.x; y0 = value.y; } + } + + public Point p1 { + get { return new Point(x1, y1); } + set { x1 = value.x; y1 = value.y; } + } + + public double width { + get { return x1 - x0; } + set { x1 = x0 + value; } + } + + public double height { + get { return y1 - y0; } + set { y1 = y0 + value; } + } + + public bool empty { + get { return width <= Geometry.precision || height <= Geometry.precision; } + } + + public Rectangle(double x0, double y0, double x1, double y1) { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + } + public Rectangle(double x, double y): + this(x, y, x, y) { } + public Rectangle(Point p): + this(p.x, p.y) { } + public Rectangle(Point p0, Point p1): + this(p0.x, p0.y, p1.x, p1.y) { } + public Rectangle(System.Drawing.Rectangle rect): + this(rect.Left, rect.Top, rect.Right, rect.Bottom) { } + public Rectangle(System.Drawing.RectangleF rect): + this(rect.Left, rect.Top, rect.Right, rect.Bottom) { } + + public Rectangle expand(Point p, double radius = 0.0) { + return new Rectangle( + Math.Min(x0, p.x), + Math.Min(y0, p.y), + Math.Max(x1, p.x), + Math.Max(y1, p.y) ); + } + + public Rectangle inflate(double x, double y) { + return new Rectangle( + x0 - x, + y0 - y, + x1 + x, + y1 + y ); + } + + public Rectangle inflate(double size) { + return inflate(size, size); + } + + public static Rectangle operator| (Rectangle a, Rectangle b) { + Rectangle rect = a.empty ? b + : b.empty ? a + : new Rectangle( Math.Min(a.x0, b.x0), + Math.Min(a.y0, b.y0), + Math.Max(a.x1, b.x1), + Math.Max(a.y1, b.y1) ); + return rect.empty ? new Rectangle() : rect; + } + + public static Rectangle operator& (Rectangle a, Rectangle b) { + Rectangle rect = a.empty ? b + : b.empty ? a + : new Rectangle( Math.Max(a.x0, b.x0), + Math.Max(a.y0, b.y0), + Math.Min(a.x1, b.x1), + Math.Min(a.y1, b.y1) ); + return rect.empty ? new Rectangle() : rect; + } + + public System.Drawing.Rectangle toInt() { + System.Drawing.Rectangle rect = new System.Drawing.Rectangle( + (int)Math.Floor(x0), + (int)Math.Floor(y0), + (int)Math.Ceiling(x1) - (int)Math.Floor(x0), + (int)Math.Ceiling(y1) - (int)Math.Floor(y0) ); + if (empty) { + rect.Width = Math.Min(0, rect.Width); + rect.Height = Math.Min(0, rect.Height); + } + return rect; + } + + public System.Drawing.RectangleF toFloat() { + return new System.Drawing.RectangleF((float)x0, (float)y0, (float)(x1 - x0), (float)(y1 - y0)); + } + } +} + diff --git a/mono/Assistance/Track.cs b/mono/Assistance/Track.cs new file mode 100644 index 0000000..653ebf8 --- /dev/null +++ b/mono/Assistance/Track.cs @@ -0,0 +1,32 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; + +namespace Assistance { + public class Track { + public static readonly Pen pen = new Pen(Brushes.DarkGreen, 3f); + public static readonly Pen penPreview = new Pen(new SolidBrush(Color.FromArgb(64, Color.DarkGreen)), 1f); + + public readonly List points = new List(); + + public Rectangle getBounds() { + if (points.Count == 0) + return new Rectangle(); + Rectangle bounds = new Rectangle(points[0]); + foreach(Point p in points) + bounds = bounds.expand(p); + return bounds.inflate(Math.Max(pen.Width, penPreview.Width) + 2.0); + } + + public void draw(Graphics g, bool preview = false) { + if (points.Count < 2) + return; + PointF[] ps = new PointF[points.Count]; + for(int i = 0; i < ps.Length; ++i) + ps[i] = points[i].toFloat(); + g.DrawLines(preview ? penPreview : pen, ps); + } + } +} + diff --git a/mono/Assistance/Workarea.cs b/mono/Assistance/Workarea.cs new file mode 100644 index 0000000..317bdd4 --- /dev/null +++ b/mono/Assistance/Workarea.cs @@ -0,0 +1,78 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; +using System.Linq; + +namespace Assistance { + public class Workarea { + public readonly List assistants = new List(); + public readonly List points = new List(); + public readonly Canvas canvas = new Canvas(); + + public ActivePoint ActivePoint = null; + + public ActivePoint findPoint(Point position) { + foreach(ActivePoint point in points.Reverse()) + if (point.isInside(position)) + return point; + return null; + } + + public void getGuidelines(List outGuidelines, Point target) { + foreach(Assistant assistant in assistants) + assistant.getGuidelines(outGuidelines, target); + } + + public void draw(Graphics g, ActivePoint activePoint, Point target, Track track) { + // canvas + canvas.draw(g); + + // guidelines and track + List guidelines = new List(); + if (track != null && track.points.Count > 0) { + getGuidelines(guidelines, track.points[0]); + foreach(Guideline gl in guidelines) + gl.draw(g); + Guideline guideline = Guideline.findBest(guidelines, track); + if (guideline != null) { + track.draw(g, true); + guideline.modifyTrack(track).draw(g); + guideline.draw(g, true); + } else { + track.draw(g); + } + } else { + getGuidelines(guidelines, target); + foreach(Guideline guideline in guidelines) + guideline.draw(g); + } + + // assistants + foreach(Assistant assistant in assistants) + assistant.draw(g); + + // assistant active points + foreach(ActivePoint point in points) + point.draw(g, activePoint == point); + } + + public Track modifyTrack(Track track) { + if (track.points.Count < 1) + return track; + + List guidelines = new List(); + getGuidelines(guidelines, track.points[0]); + Guideline guideline = Guideline.findBest(guidelines, track); + if (guideline == null) + return track; + + return guideline.modifyTrack(track); + } + + public void paintTrack(Track track) { + canvas.paintTrack( modifyTrack(track) ); + } + } +} +