diff --git a/mono/Assistance/ActivePoint.cs b/mono/Assistance/ActivePoint.cs
new file mode 100644
index 0000000..fbecc1f
--- /dev/null
+++ b/mono/Assistance/ActivePoint.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Collections.Generic;
+
+namespace Assistance {
+ public class ActivePoint {
+ public enum Mode {
+ Common = 0,
+ Active = 1
+ }
+
+ public enum Type {
+ Circle,
+ CircleFill,
+ CircleCross,
+ };
+
+ public static readonly double radius = 10.0;
+ public static readonly double crossSize = 1.2*radius;
+ public static readonly Pen[] pens = new Pen[] { Pens.Gray, Pens.Blue };
+ public static readonly Brush[] brushes = new Brush[] { Brushes.LightGray, Brushes.LightBlue };
+
+ public readonly Canvas canvas;
+ public readonly Assistant assistant;
+ public readonly Type type;
+ public Point position;
+
+ public ActivePoint(Assistant assistant, Type type, Point position = new Point()) {
+ this.canvas = assistant.canvas;
+ this.assistant = assistant;
+ this.type = type;
+ this.position = position;
+ canvas.points.Add(this);
+ assistant.points.Add(this);
+ }
+
+ public bool isInside(Point p) {
+ return (position - p).lenSqr() <= radius*radius;
+ }
+
+ public void bringToFront() {
+ assistant.bringToFront();
+ assistant.points.Remove(this);
+ assistant.points.Add(this);
+ canvas.points.Remove(this);
+ canvas.points.Add(this);
+ }
+
+ private void drawCircle(Graphics g, Mode mode) {
+ g.DrawEllipse(pens[(int)mode], (float)(position.x - radius), (float)(position.y - radius), (float)(2.0*radius), (float)(2.0*radius));
+ }
+
+ private void fillCircle(Graphics g, Mode mode) {
+ g.FillEllipse(brushes[(int)mode], (float)(position.x - radius), (float)(position.y - radius), (float)(2.0*radius), (float)(2.0*radius));
+ }
+
+ private void drawCross(Graphics g, Mode mode) {
+ g.DrawLine(pens[(int)mode], (float)(position.x - crossSize), (float)position.y, (float)(position.x + crossSize), (float)position.y);
+ g.DrawLine(pens[(int)mode], (float)position.x, (float)(position.y - crossSize), (float)position.x, (float)(position.y + crossSize));
+ }
+
+ public void draw(Graphics g, Mode mode) {
+ switch(type) {
+ case Type.Circle:
+ drawCircle(g, mode);
+ break;
+ case Type.CircleFill:
+ fillCircle(g, mode);
+ drawCircle(g, mode);
+ break;
+ case Type.CircleCross:
+ drawCircle(g, mode);
+ drawCross(g, mode);
+ break;
+ }
+ }
+ }
+}
+
diff --git a/mono/Assistance/Assistance.csproj b/mono/Assistance/Assistance.csproj
new file mode 100644
index 0000000..f398257
--- /dev/null
+++ b/mono/Assistance/Assistance.csproj
@@ -0,0 +1,50 @@
+
+
+
+ Debug
+ x86
+ 10.0.0
+ 2.0
+ {77CD6953-34F0-4125-9DF5-957ECF0E23F0}
+ Exe
+ Assistance
+ Assistance
+ v4.5
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ x86
+ false
+
+
+ none
+ true
+ bin\Release
+ prompt
+ 4
+ x86
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mono/Assistance/Assistance.sln b/mono/Assistance/Assistance.sln
new file mode 100644
index 0000000..43bd5bc
--- /dev/null
+++ b/mono/Assistance/Assistance.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assistance", "Assistance.csproj", "{77CD6953-34F0-4125-9DF5-957ECF0E23F0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {77CD6953-34F0-4125-9DF5-957ECF0E23F0}.Debug|x86.ActiveCfg = Debug|x86
+ {77CD6953-34F0-4125-9DF5-957ECF0E23F0}.Debug|x86.Build.0 = Debug|x86
+ {77CD6953-34F0-4125-9DF5-957ECF0E23F0}.Release|x86.ActiveCfg = Release|x86
+ {77CD6953-34F0-4125-9DF5-957ECF0E23F0}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = Assistance.csproj
+ EndGlobalSection
+EndGlobal
diff --git a/mono/Assistance/Assistant.cs b/mono/Assistance/Assistant.cs
new file mode 100644
index 0000000..c6abe90
--- /dev/null
+++ b/mono/Assistance/Assistant.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Collections.Generic;
+
+namespace Assistance {
+ public class Assistant {
+ public static readonly double maxLen = 1000.0;
+ public static readonly int gridPointsCount = 100;
+ public static readonly Pen pen = Pens.Gray;
+ public static readonly Pen guidePen = Pens.LightGray;
+
+ public readonly Canvas canvas;
+ public readonly List points = new List();
+
+ public Assistant(Canvas canvas) {
+ this.canvas = canvas;
+ canvas.assistants.Add(this);
+ }
+
+ public void remove() {
+ foreach(ActivePoint point in points)
+ canvas.points.Remove(point);
+ canvas.assistants.Remove(this);
+ }
+
+ public void bringToFront() {
+ canvas.assistants.Remove(this);
+ canvas.assistants.Add(this);
+ }
+
+ public double getMaxLen() {
+ double l = 0.0;
+ foreach(ActivePoint point in points)
+ l = Math.Max(l, point.position.len());
+ return maxLen + l;
+ }
+
+ public virtual void onMovePoint(ActivePoint point, Point position) {
+ point.position = position;
+ }
+
+ public virtual Point[] getGridPoints(Point target, bool truncate) { return new Point[0]; }
+
+ public Point[] getGridPoints(Point target) { return getGridPoints(target, false); }
+
+ public virtual void draw(Graphics g) { }
+
+ public virtual void drawGuidlines(Graphics g, Point target, bool truncate) { }
+
+ public void drawGuidlines(Graphics g, Point target) {
+ drawGuidlines(g, target, false);
+ }
+ }
+}
+
diff --git a/mono/Assistance/Canvas.cs b/mono/Assistance/Canvas.cs
new file mode 100644
index 0000000..a211fb1
--- /dev/null
+++ b/mono/Assistance/Canvas.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Assistance {
+ public class Canvas {
+ public readonly List assistants = new List();
+ public readonly List points = new List();
+
+ 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 drawGuidlines(Graphics g, Point point) {
+ foreach(Assistant assistant in assistants)
+ assistant.drawGuidlines(g, point);
+ }
+
+ public void drawAssistants(Graphics g) {
+ foreach(Assistant assistant in assistants)
+ assistant.draw(g);
+ }
+
+ public void drawPoints(Graphics g, ActivePoint activePoint) {
+ foreach(ActivePoint point in points)
+ point.draw(g, activePoint == point ? ActivePoint.Mode.Active : ActivePoint.Mode.Common);
+ }
+
+ public void draw(Graphics g, ActivePoint activePoint, Point guidlinesPoint) {
+ drawGuidlines(g, guidlinesPoint);
+ drawAssistants(g);
+ drawPoints(g, activePoint);
+ }
+
+ public void draw(Graphics g, ActivePoint activePoint) {
+ drawAssistants(g);
+ drawPoints(g, activePoint);
+ }
+ }
+}
+
diff --git a/mono/Assistance/Grid.cs b/mono/Assistance/Grid.cs
new file mode 100644
index 0000000..6bd02df
--- /dev/null
+++ b/mono/Assistance/Grid.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Assistance {
+ public class Grid: Assistant {
+ public ActivePoint center;
+
+ public Grid(Canvas 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, true))
+ foreach(Assistant a in canvas.assistants)
+ if (a != assistant)
+ a.drawGuidlines(g, p, true);
+ }
+ }
+}
+
diff --git a/mono/Assistance/MainWindow.cs b/mono/Assistance/MainWindow.cs
new file mode 100644
index 0000000..f1e294f
--- /dev/null
+++ b/mono/Assistance/MainWindow.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Windows.Forms;
+using System.Collections.Generic;
+
+namespace Assistance {
+ public class MainWindow : Form {
+ static public void Main() { Application.Run(new MainWindow()); }
+
+ Bitmap bitmap = new Bitmap(1, 1);
+ Canvas canvas = new Canvas();
+ bool dragging = false;
+ ActivePoint activePoint;
+ Point offset;
+ Point cursor;
+
+ public MainWindow() {
+ Paint += onPaint;
+ MouseMove += onMouseMove;
+ MouseDown += onMouseDown;
+ MouseUp += onMouseUp;
+ KeyDown += onKeyDown;
+ WindowState = FormWindowState.Maximized;
+ }
+
+ protected override void OnPaintBackground(PaintEventArgs e) { }
+
+ public void onPaint(Object sender, PaintEventArgs e) {
+ if (bitmap.Size != ClientSize)
+ bitmap = new Bitmap(ClientSize.Width, ClientSize.Height);
+ Graphics g = Graphics.FromImage(bitmap);
+ g.Clear(Color.White);
+ draw(g);
+ g.Flush();
+ e.Graphics.DrawImageUnscaled(bitmap, new Rectangle(0, 0, ClientSize.Width, ClientSize.Height));
+ }
+
+ public Point windowToCanvas(Point p) {
+ return new Point(p.x - ClientSize.Width/2.0, p.y - ClientSize.Height/2.0);
+ }
+
+ public Point canvasToWindow(Point p) {
+ return new Point(p.x + ClientSize.Width/2.0, p.y + ClientSize.Height/2.0);
+ }
+
+ private void beginDrag() {
+ dragging = true;
+ offset = activePoint.position - cursor;
+ activePoint.bringToFront();
+ }
+
+ private void endDrag() {
+ dragging = false;
+ offset = new Point();
+ }
+
+ public void onKeyDown(Object sender, KeyEventArgs e) {
+ switch(e.KeyCode) {
+ case Keys.D1:
+ new VanishingPoint(canvas, cursor);
+ break;
+ case Keys.D2:
+ new Grid(canvas, cursor);
+ break;
+ case Keys.Delete:
+ if (activePoint != null)
+ activePoint.assistant.remove();
+ endDrag();
+ break;
+ }
+ endDrag();
+ Invalidate();
+ }
+
+ public void onMouseDown(Object sender, MouseEventArgs e) {
+ cursor = windowToCanvas(new Point(e.Location.X, e.Location.Y));
+ if (e.Button == MouseButtons.Left) {
+ activePoint = canvas.findPoint(cursor);
+ if (activePoint != null)
+ beginDrag();
+ }
+ Invalidate();
+ }
+
+ public void onMouseUp(Object sender, MouseEventArgs e) {
+ cursor = windowToCanvas(new Point(e.X, e.Y));
+ if (e.Button == MouseButtons.Left)
+ endDrag();
+ if (!dragging)
+ activePoint = canvas.findPoint(cursor);
+ Invalidate();
+ }
+
+ public void onMouseMove(Object sender, MouseEventArgs e) {
+ cursor = windowToCanvas(new Point(e.Location.X, e.Location.Y));
+ if (dragging) {
+ activePoint.assistant.onMovePoint(activePoint, cursor + offset);
+ } else {
+ activePoint = canvas.findPoint(cursor);
+ }
+ Invalidate();
+ }
+
+ public void draw(Graphics g) {
+ g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
+ g.TranslateTransform(0.5f*ClientSize.Width, 0.5f*ClientSize.Height);
+ canvas.draw(g, activePoint, cursor + offset);
+ }
+ }
+}
+
diff --git a/mono/Assistance/Point.cs b/mono/Assistance/Point.cs
new file mode 100644
index 0000000..6ab9fe6
--- /dev/null
+++ b/mono/Assistance/Point.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace Assistance {
+ public struct Point {
+ public static readonly double precision = 0.01;
+
+ public double x;
+ public double y;
+
+ public Point(double x, double y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public static Point operator+ (Point a, Point b) {
+ return new Point(a.x + b.x, a.y + b.y);
+ }
+
+ public static Point operator- (Point a, Point b) {
+ return new Point(a.x - b.x, a.y - b.y);
+ }
+
+ public static Point operator* (Point a, double b) {
+ return new Point(a.x*b, a.y*b);
+ }
+
+ public static Point operator* (double b, Point a) {
+ return a*b;
+ }
+
+ public static Point operator/ (Point a, double b) {
+ return new Point(a.x/b, a.y/b);
+ }
+
+ public static double dot(Point a, Point b) {
+ return a.x*b.x + a.y*b.y;
+ }
+
+ public bool isEqual(Point other) {
+ return (this - other).lenSqr() <= precision*precision;
+ }
+
+ public double lenSqr() {
+ return x*x + y*y;
+ }
+
+ public double len() {
+ return Math.Sqrt(lenSqr());
+ }
+
+ public Point normalize() {
+ double l = len();
+ return l > precision*precision*precision ? this/l : this;
+ }
+
+ public System.Drawing.PointF toFloat() {
+ return new System.Drawing.PointF((float)x, (float)y);
+ }
+
+ public System.Drawing.Point toInt() {
+ return new System.Drawing.Point((int)Math.Floor(x), (int)Math.Floor(y));
+ }
+ }
+}
+
diff --git a/mono/Assistance/VanishingPoint.cs b/mono/Assistance/VanishingPoint.cs
new file mode 100644
index 0000000..c14d254
--- /dev/null
+++ b/mono/Assistance/VanishingPoint.cs
@@ -0,0 +1,120 @@
+using System;
+
+namespace Assistance {
+ public class VanishingPoint: Assistant {
+ public ActivePoint center;
+ public ActivePoint a0;
+ public ActivePoint a1;
+ public ActivePoint b0;
+ public ActivePoint b1;
+ public ActivePoint step;
+
+ public VanishingPoint(Canvas 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;
+
+ 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 drawGuidlines(System.Drawing.Graphics g, Point target, bool truncate) {
+ if (truncate) {
+ g.DrawLine(guidePen, center.position.toFloat(), target.toFloat());
+ } else {
+ Point p = (target - center.position).normalize()*getMaxLen() + center.position;
+ g.DrawLine(guidePen, center.position.toFloat(), p.toFloat());
+ }
+ }
+ }
+}
+