diff --git a/mono/Contours/Contours.csproj b/mono/Contours/Contours.csproj
index 5c4af5f..2a081d3 100644
--- a/mono/Contours/Contours.csproj
+++ b/mono/Contours/Contours.csproj
@@ -37,6 +37,7 @@
+
@@ -47,4 +48,9 @@
+
+
+ Always
+
+
\ No newline at end of file
diff --git a/mono/Contours/MainForm.cs b/mono/Contours/MainForm.cs
index bdfabcc..7d52e58 100644
--- a/mono/Contours/MainForm.cs
+++ b/mono/Contours/MainForm.cs
@@ -6,7 +6,30 @@ using System.Drawing;
namespace Contours {
public class MainForm: Form {
+ Button bTest;
+ ComboBox cbTests;
+ ComboBox cbViews;
+
public MainForm() {
+ bTest = new Button();
+ bTest.Left = 20;
+ bTest.Top = 20;
+ bTest.Text = "test";
+ bTest.Click += bTestClicked;
+ Controls.Add(bTest);
+
+ cbTests = new ComboBox();
+ cbTests.Left = bTest.Right + 20;
+ cbTests.Top = 20;
+ cbTests.SelectedIndexChanged += cbTestsChanged;
+ Controls.Add(cbTests);
+
+ cbViews = new ComboBox();
+ cbViews.Left = cbTests.Right + 20;
+ cbViews.Top = 20;
+ cbViews.SelectedIndexChanged += cbViewsChanged;
+ Controls.Add(cbViews);
+
MouseDown += mouseDown;
MouseMove += mouseMove;
MouseUp += mouseUp;
@@ -16,6 +39,33 @@ namespace Contours {
bool drawing = false;
List> contours = new List>();
+ void bTestClicked(object sender, EventArgs e) {
+ Test.loadTestsFromFile("tests.txt");
+ bool success = Test.runAll();
+ Test.saveReport("report.txt");
+
+ foreach(Test test in Test.tests)
+ cbTests.Items.Add(test.name);
+
+ if (!success) MessageBox.Show("Tests failed");
+ }
+
+ void cbTestsChanged(object sender, EventArgs e) {
+ cbViews.Items.Clear();
+ foreach(Test test in Test.tests) {
+ if (cbTests.Text == test.name) {
+ foreach(string name in test.input.Keys)
+ cbViews.Items.Add("i: " + name);
+ foreach(string name in test.output.Keys)
+ cbViews.Items.Add("o: " + name);
+ }
+ }
+ }
+
+ void cbViewsChanged(object sender, EventArgs e) {
+ Refresh();
+ }
+
private void mouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
contours.Add(new List());
@@ -43,12 +93,44 @@ namespace Contours {
}
}
+ void drawContour(Graphics g, Color color, List c) {
+ if (c != null && c.Count >= 3) {
+ g.DrawLines(new Pen(new SolidBrush(color)), c.ToArray());
+ g.DrawLine(new Pen(new SolidBrush(color)), c.First(), c.Last());
+ }
+ }
+
+ void drawContour(Graphics g, Color color, List c) {
+ if (c != null && c.Count >= 3) {
+ g.DrawLines(new Pen(new SolidBrush(color)), c.ToArray());
+ g.DrawLine(new Pen(new SolidBrush(color)), c.First(), c.Last());
+ }
+ }
+
private void paint(object sender, PaintEventArgs e) {
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
- foreach(List c in contours) {
- if (c != null && c.Count >= 3) {
- e.Graphics.DrawLines(Pens.Black, c.ToArray());
- e.Graphics.DrawLine(Pens.Black, c.First(), c.Last());
+ foreach(List c in contours)
+ drawContour(e.Graphics, Color.Gray, c);
+
+ List>> testContours = null;
+ foreach(Test test in Test.tests) {
+ if (cbTests.Text == test.name) {
+ foreach(KeyValuePair>>> pair in test.input)
+ if (cbViews.Text == "i: " + pair.Key)
+ testContours = pair.Value;
+ foreach(KeyValuePair>>> pair in test.output)
+ if (cbViews.Text == "o: " + pair.Key)
+ testContours = pair.Value;
+ }
+ }
+
+ if (testContours != null) {
+ foreach(List> group in testContours) {
+ Color color = Color.Black;
+ foreach(List c in group) {
+ drawContour(e.Graphics, color, c);
+ color = Color.Blue;
+ }
}
}
}
diff --git a/mono/Contours/Shape.cs b/mono/Contours/Shape.cs
index 099ea8b..ed62ecf 100644
--- a/mono/Contours/Shape.cs
+++ b/mono/Contours/Shape.cs
@@ -5,6 +5,8 @@ using System.Drawing;
namespace Contours {
public class Shape {
+ class Exception: System.Exception { }
+
class Position {
public Point point;
public readonly Circuit links;
@@ -77,6 +79,13 @@ namespace Contours {
setContours(new IEnumerable[] { contour });
}
+ public void setContours(IEnumerable>> contours) {
+ List> list = new List>();
+ foreach(IEnumerable> c in contours)
+ list.AddRange(c);
+ setContours(list);
+ }
+
public void setContours(IEnumerable> contours) {
clear();
foreach(IEnumerable contour in contours) {
diff --git a/mono/Contours/Test.cs b/mono/Contours/Test.cs
new file mode 100644
index 0000000..cb00ac3
--- /dev/null
+++ b/mono/Contours/Test.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace Contours {
+ public class Test {
+ class Exception: System.Exception { }
+
+ public string name;
+
+ public readonly Dictionary>>> input = new Dictionary>>>();
+ public readonly Dictionary>>> output = new Dictionary>>>();
+ public readonly Dictionary results = new Dictionary();
+ public bool result = false;
+
+ public static readonly List tests = new List();
+
+ void check(string name, List>> calculatedContours) {
+ output.Add(name, calculatedContours);
+ results.Add(name, compareContours(input[name], output[name]));
+ if (!results[name]) result = false;
+ }
+
+ public bool run() {
+ result = true;
+ Shape a = new Shape();
+ Shape b = new Shape();
+ a.setContours(input["a"]);
+ b.setContours(input["b"]);
+ check("add", Shape.add(a, b).getContours());
+ check("subtract", Shape.subtract(a, b).getContours());
+ check("xor", Shape.xor(a, b).getContours());
+ check("intersection", Shape.intersection(a, b).getContours());
+ return result;
+ }
+
+ public static bool compareContours(List a, List b) {
+ if (a.Count == b.Count) {
+ for(int offset = 0; offset < a.Count; ++offset) {
+ bool equal = true;
+ for(int i = 0; i < a.Count; ++i)
+ if (a[(i + offset)%a.Count] != b[i])
+ { equal = false; break; }
+ if (equal) return true;
+ }
+ }
+ return false;
+ }
+
+ public static bool compareContours(List> a, List> b) {
+ if (a.Count != b.Count) return false;
+ if (a.Count == 0) return true;
+ if (!compareContours(a[0], b[0])) return false;
+ bool[] compared = new bool[a.Count];
+ for(int i = 1; i < a.Count; ++i) {
+ bool equal = false;
+ for(int j = i + 1; j < b.Count; ++j) {
+ if (!compared[i] && compareContours(a[i], b[j]))
+ { equal = true; compared[j] = true; break; }
+ }
+ if (!equal) return false;
+ }
+ return true;
+ }
+
+ public static bool compareContours(List>> a, List>> b) {
+ if (a.Count != b.Count) return false;
+ bool[] compared = new bool[a.Count];
+ for(int i = 0; i < a.Count; ++i) {
+ bool equal = false;
+ for(int j = i + 1; j < b.Count; ++j) {
+ if (!compared[i] && compareContours(a[i], b[j]))
+ { equal = true; compared[j] = true; break; }
+ }
+ if (!equal) return false;
+ }
+ return true;
+ }
+
+ class Loader {
+ public string text;
+ public int position = 0;
+
+ void error() { throw new Exception(); }
+ void assert(bool expr) { if (!expr) error(); }
+
+ void skipSpaces() {
+ while(position < text.Length && char.IsWhiteSpace(text[position])) ++position;
+ }
+
+ int loadInt() {
+ skipSpaces();
+ int startPosition = position;
+ while(position < text.Length && char.IsDigit(text[position])) ++position;
+ assert(startPosition < position);
+ return int.Parse(text.Substring(startPosition, position-startPosition));
+ }
+
+ string tryLoadKey(string key) {
+ return tryLoadKey(new string[] { key });
+ }
+
+ string tryLoadKey(string key0, string key1) {
+ return tryLoadKey(new string[] { key0, key1 });
+ }
+
+ string tryLoadKey(string[] keys) {
+ skipSpaces();
+ foreach(string key in keys)
+ if (text.Substring(position, key.Length) == key)
+ { position += key.Length; return key; }
+ return null;
+ }
+
+ string loadKey(string key) {
+ return loadKey(new string[] { key });
+ }
+
+ string loadKey(string key0, string key1) {
+ return loadKey(new string[] { key0, key1 });
+ }
+
+ string loadKey(string[] keys) {
+ string result = tryLoadKey(keys);
+ assert(result != null);
+ return result;
+ }
+
+ Point loadPoint() {
+ loadKey("(");
+ int x = loadInt();
+ loadKey(",");
+ int y = loadInt();
+ loadKey(")");
+ return new Point(x, y);
+ }
+
+ List loadPointList() {
+ List list = new List();
+ loadKey("(");
+ if (tryLoadKey(")") == null) do {
+ list.Add(loadPoint());
+ } while(loadKey(",", ")") == ",");
+ return list;
+ }
+
+ List> loadPointListList() {
+ List> list = new List>();
+ loadKey("(");
+ if (tryLoadKey(")") == null) do {
+ list.Add(loadPointList());
+ } while(loadKey(",", ")") == ",");
+ return list;
+ }
+
+ List>> loadPointListListList() {
+ List>> list = new List>>();
+ loadKey("(");
+ if (tryLoadKey(")") == null) do {
+ list.Add(loadPointListList());
+ } while(loadKey(",", ")") == ",");
+ return list;
+ }
+
+ string loadFieldName() {
+ skipSpaces();
+ int startPosition = position;
+ while(position < text.Length && char.IsLetterOrDigit(text[position])) ++position;
+ assert(startPosition < position);
+ return text.Substring(startPosition, position-startPosition);
+ }
+
+ string loadName() {
+ string name = "";
+ loadKey("(");
+ while(tryLoadKey(")") == null)
+ name += text[position++];
+ return name.Trim();
+ }
+
+ Test loadTest() {
+ Test test = new Test();
+ loadKey("{");
+ while(tryLoadKey("}") == null) {
+ string name = loadFieldName();
+ loadKey(":");
+ if (name == "name")
+ test.name = loadName();
+ else
+ test.input.Add(name, loadPointListListList());
+ }
+ return test;
+ }
+
+ public List loadTestListToEof() {
+ List list = new List();
+ while(true) {
+ skipSpaces();
+ if (position >= text.Length) break;
+ list.Add(loadTest());
+ }
+ return list;
+ }
+ };
+
+ static void loadTests(string text) {
+ Loader loader = new Loader();
+ loader.text = text;
+ tests.Clear();
+ tests.AddRange(loader.loadTestListToEof());
+ }
+
+ public static void loadTestsFromFile(string filename) {
+ loadTests(System.IO.File.ReadAllText(filename));
+ }
+
+ public static bool runAll() {
+ bool result = true;
+ foreach(Test test in tests)
+ if (!test.run()) result = false;
+ return result;
+ }
+
+ static string resultToString(string name, bool result) {
+ return name + ": " + (result ? "+" : "FAILED") + "\n";
+ }
+
+ public static string makeReport() {
+ string report = "";
+ foreach(Test test in tests) {
+ report += resultToString(test.name, test.result);
+ foreach(KeyValuePair pair in test.results)
+ report += resultToString(" " + pair.Key, pair.Value);
+ }
+ return report;
+ }
+
+ public static void saveReport(string filename) {
+ System.IO.File.WriteAllText(filename, makeReport());
+ }
+ }
+}
+
diff --git a/mono/Contours/tests.txt b/mono/Contours/tests.txt
new file mode 100644
index 0000000..86d1dce
--- /dev/null
+++ b/mono/Contours/tests.txt
@@ -0,0 +1,17 @@
+{
+ name:
+ (Two squares)
+ a:
+ ( ( ((0, 0), (200, 0), (200, 200), (0, 200)) ) )
+ b:
+ ((((100, 100), (300, 100), (300, 300), (100, 300))))
+ add:
+ ((((0, 0), (200, 0), (200, 100), (300, 100), (300, 300), (100, 300), (100, 200), (0, 200))))
+ subtract:
+ ((((0, 0), (200, 0), (200, 100), (100, 100), (100, 200), (0, 200))))
+ xor:
+ (( ((0, 0), (200, 0), (200, 100), (100, 100), (100, 200), (0, 200)),
+ ((200, 200), (200, 100), (300, 100), (300, 300), (100, 300), (100, 200)) ))
+ intersection:
+ ((((100, 100), (200, 100), (200, 200), (100, 200))))
+}