|
|
80bc9b |
/*
|
|
|
80bc9b |
......... 2015 Ivan Mahonin
|
|
|
80bc9b |
|
|
|
80bc9b |
This program is free software: you can redistribute it and/or modify
|
|
|
80bc9b |
it under the terms of the GNU General Public License as published by
|
|
|
80bc9b |
the Free Software Foundation, either version 3 of the License, or
|
|
|
80bc9b |
(at your option) any later version.
|
|
|
80bc9b |
|
|
|
80bc9b |
This program is distributed in the hope that it will be useful,
|
|
|
80bc9b |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
80bc9b |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
80bc9b |
GNU General Public License for more details.
|
|
|
80bc9b |
|
|
|
80bc9b |
You should have received a copy of the GNU General Public License
|
|
|
80bc9b |
along with this program. If not, see <http: licenses="" www.gnu.org="">.</http:>
|
|
|
80bc9b |
*/
|
|
|
80bc9b |
|
|
|
7c6265 |
using System;
|
|
|
7c6265 |
using System.Collections.Generic;
|
|
|
7c6265 |
using System.Drawing;
|
|
|
7c6265 |
|
|
|
7c6265 |
namespace Contours {
|
|
|
7c6265 |
public class Test {
|
|
|
7c6265 |
class Exception: System.Exception { }
|
|
|
7c6265 |
|
|
|
7c6265 |
public string name;
|
|
|
7c6265 |
|
|
|
7c6265 |
public readonly Dictionary<string, list<list<list<point="">>>> input = new Dictionary<string, list<list<list<point="">>>>();</string,></string,>
|
|
|
7c6265 |
public readonly Dictionary<string, list<list<list<point="">>>> output = new Dictionary<string, list<list<list<point="">>>>();</string,></string,>
|
|
|
7c6265 |
public readonly Dictionary<string, bool=""> results = new Dictionary<string, bool="">();</string,></string,>
|
|
|
7c6265 |
public bool result = false;
|
|
|
7c6265 |
|
|
|
7c6265 |
public static readonly List<test> tests = new List<test>();</test></test>
|
|
|
7c6265 |
|
|
|
05068e |
void check(string name, Shape shape) {
|
|
|
05068e |
if (!input.ContainsKey(name)) return;
|
|
|
05068e |
List<list<list<point>>> contours = null;</list<list<point>
|
|
|
05068e |
try {
|
|
|
05068e |
contours = shape.getContours();
|
|
|
05068e |
} catch(System.Exception) { }
|
|
|
05068e |
output.Add(name, contours);
|
|
|
7c6265 |
results.Add(name, compareContours(input[name], output[name]));
|
|
|
7c6265 |
if (!results[name]) result = false;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
05068e |
Shape tryCreateShape(List<list<list<point>>> contours) {</list<list<point>
|
|
|
05068e |
try {
|
|
|
05068e |
Shape shape = new Shape();
|
|
|
05068e |
shape.setContours(contours);
|
|
|
05068e |
return shape;
|
|
|
05068e |
} catch (System.Exception) {
|
|
|
05068e |
return null;
|
|
|
05068e |
}
|
|
|
05068e |
}
|
|
|
05068e |
|
|
|
30455f |
Shape tryCombineShapes(Shape.CombinationMode mode, List<list<list<point>>> a, List<list<list<point>>> b) {</list<list<point></list<list<point>
|
|
|
05068e |
try {
|
|
|
05068e |
Shape sa = new Shape();
|
|
|
05068e |
Shape sb = new Shape();
|
|
|
05068e |
sa.setContours(a);
|
|
|
05068e |
sb.setContours(b);
|
|
|
30455f |
return Shape.combine(mode, sa, sb);
|
|
|
05068e |
} catch (System.Exception) {
|
|
|
05068e |
return null;
|
|
|
05068e |
}
|
|
|
05068e |
}
|
|
|
05068e |
|
|
|
7c6265 |
public bool run() {
|
|
|
7c6265 |
result = true;
|
|
|
05068e |
|
|
|
05068e |
List<list<list<point>>> a = null;</list<list<point>
|
|
|
05068e |
List<list<list<point>>> b = null;</list<list<point>
|
|
|
05068e |
|
|
|
296d46 |
if (input.ContainsKey("badA")) a = input["badA"]; else
|
|
|
05068e |
if (input.ContainsKey("a")) a = input["a"];
|
|
|
296d46 |
if (input.ContainsKey("badB")) b = input["badB"]; else
|
|
|
05068e |
if (input.ContainsKey("b")) b = input["b"];
|
|
|
05068e |
|
|
|
05068e |
if (a != null)
|
|
|
05068e |
check("a", tryCreateShape(a));
|
|
|
05068e |
if (b != null)
|
|
|
05068e |
check("b", tryCreateShape(b));
|
|
|
05068e |
|
|
|
05068e |
if (a != null && b != null) {
|
|
|
30455f |
check("add", tryCombineShapes(Shape.CombinationMode.Add, a, b));
|
|
|
30455f |
check("subtract", tryCombineShapes(Shape.CombinationMode.Subtract, a, b));
|
|
|
30455f |
check("intersection", tryCombineShapes(Shape.CombinationMode.Intersection, a, b));
|
|
|
30455f |
check("xor", tryCombineShapes(Shape.CombinationMode.Xor, a, b));
|
|
|
05068e |
}
|
|
|
05068e |
|
|
|
7c6265 |
return result;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public static bool compareContours(List<point> a, List<point> b) {</point></point>
|
|
|
05068e |
if (a == null || b == null) return false;
|
|
|
7c6265 |
if (a.Count == b.Count) {
|
|
|
7c6265 |
for(int offset = 0; offset < a.Count; ++offset) {
|
|
|
7c6265 |
bool equal = true;
|
|
|
7c6265 |
for(int i = 0; i < a.Count; ++i)
|
|
|
7c6265 |
if (a[(i + offset)%a.Count] != b[i])
|
|
|
7c6265 |
{ equal = false; break; }
|
|
|
7c6265 |
if (equal) return true;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
}
|
|
|
7c6265 |
return false;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public static bool compareContours(List<list<point>> a, List<list<point>> b) {</list<point></list<point>
|
|
|
05068e |
if (a == null || b == null) return false;
|
|
|
7c6265 |
if (a.Count != b.Count) return false;
|
|
|
7c6265 |
if (a.Count == 0) return true;
|
|
|
7c6265 |
if (!compareContours(a[0], b[0])) return false;
|
|
|
7c6265 |
bool[] compared = new bool[a.Count];
|
|
|
7c6265 |
for(int i = 1; i < a.Count; ++i) {
|
|
|
7c6265 |
bool equal = false;
|
|
|
bb5d1a |
for(int j = 1; j < b.Count; ++j) {
|
|
|
bb5d1a |
if (!compared[j] && compareContours(a[i], b[j]))
|
|
|
7c6265 |
{ equal = true; compared[j] = true; break; }
|
|
|
7c6265 |
}
|
|
|
7c6265 |
if (!equal) return false;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
return true;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public static bool compareContours(List<list<list<point>>> a, List<list<list<point>>> b) {</list<list<point></list<list<point>
|
|
|
05068e |
if (a == null || b == null) return false;
|
|
|
7c6265 |
if (a.Count != b.Count) return false;
|
|
|
7c6265 |
bool[] compared = new bool[a.Count];
|
|
|
7c6265 |
for(int i = 0; i < a.Count; ++i) {
|
|
|
7c6265 |
bool equal = false;
|
|
|
d0a1c2 |
for(int j = 0; j < b.Count; ++j) {
|
|
|
bb5d1a |
if (!compared[j] && compareContours(a[i], b[j]))
|
|
|
7c6265 |
{ equal = true; compared[j] = true; break; }
|
|
|
7c6265 |
}
|
|
|
7c6265 |
if (!equal) return false;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
return true;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
class Loader {
|
|
|
7c6265 |
public string text;
|
|
|
7c6265 |
public int position = 0;
|
|
|
7c6265 |
|
|
|
7c6265 |
void error() { throw new Exception(); }
|
|
|
7c6265 |
void assert(bool expr) { if (!expr) error(); }
|
|
|
80bc9b |
|
|
|
7c6265 |
void skipSpaces() {
|
|
|
7c6265 |
while(position < text.Length && char.IsWhiteSpace(text[position])) ++position;
|
|
|
80bc9b |
if (position < text.Length && text.Substring(position, 2) == "/*") {
|
|
|
80bc9b |
while(position < text.Length)
|
|
|
80bc9b |
if (text.Substring(position, 2) == "*/")
|
|
|
80bc9b |
{ position += 2; break; } else ++position;
|
|
|
80bc9b |
skipSpaces();
|
|
|
80bc9b |
}
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
int loadInt() {
|
|
|
7c6265 |
skipSpaces();
|
|
|
7c6265 |
int startPosition = position;
|
|
|
7c6265 |
while(position < text.Length && char.IsDigit(text[position])) ++position;
|
|
|
7c6265 |
assert(startPosition < position);
|
|
|
7c6265 |
return int.Parse(text.Substring(startPosition, position-startPosition));
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string tryLoadKey(string key) {
|
|
|
7c6265 |
return tryLoadKey(new string[] { key });
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string tryLoadKey(string key0, string key1) {
|
|
|
7c6265 |
return tryLoadKey(new string[] { key0, key1 });
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string tryLoadKey(string[] keys) {
|
|
|
7c6265 |
skipSpaces();
|
|
|
7c6265 |
foreach(string key in keys)
|
|
|
7c6265 |
if (text.Substring(position, key.Length) == key)
|
|
|
7c6265 |
{ position += key.Length; return key; }
|
|
|
7c6265 |
return null;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string loadKey(string key) {
|
|
|
7c6265 |
return loadKey(new string[] { key });
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string loadKey(string key0, string key1) {
|
|
|
7c6265 |
return loadKey(new string[] { key0, key1 });
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string loadKey(string[] keys) {
|
|
|
7c6265 |
string result = tryLoadKey(keys);
|
|
|
7c6265 |
assert(result != null);
|
|
|
7c6265 |
return result;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
Point loadPoint() {
|
|
|
7c6265 |
loadKey("(");
|
|
|
7c6265 |
int x = loadInt();
|
|
|
7c6265 |
loadKey(",");
|
|
|
7c6265 |
int y = loadInt();
|
|
|
7c6265 |
loadKey(")");
|
|
|
7c6265 |
return new Point(x, y);
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
List<point> loadPointList() {</point>
|
|
|
7c6265 |
List<point> list = new List<point>();</point></point>
|
|
|
7c6265 |
loadKey("(");
|
|
|
7c6265 |
if (tryLoadKey(")") == null) do {
|
|
|
7c6265 |
list.Add(loadPoint());
|
|
|
7c6265 |
} while(loadKey(",", ")") == ",");
|
|
|
7c6265 |
return list;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
List<list<point>> loadPointListList() {</list<point>
|
|
|
7c6265 |
List<list<point>> list = new List<list<point>>();</list<point></list<point>
|
|
|
7c6265 |
loadKey("(");
|
|
|
7c6265 |
if (tryLoadKey(")") == null) do {
|
|
|
7c6265 |
list.Add(loadPointList());
|
|
|
7c6265 |
} while(loadKey(",", ")") == ",");
|
|
|
7c6265 |
return list;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
List<list<list<point>>> loadPointListListList() {</list<list<point>
|
|
|
7c6265 |
List<list<list<point>>> list = new List<list<list<point>>>();</list<list<point></list<list<point>
|
|
|
7c6265 |
loadKey("(");
|
|
|
7c6265 |
if (tryLoadKey(")") == null) do {
|
|
|
7c6265 |
list.Add(loadPointListList());
|
|
|
7c6265 |
} while(loadKey(",", ")") == ",");
|
|
|
7c6265 |
return list;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string loadFieldName() {
|
|
|
7c6265 |
skipSpaces();
|
|
|
7c6265 |
int startPosition = position;
|
|
|
7c6265 |
while(position < text.Length && char.IsLetterOrDigit(text[position])) ++position;
|
|
|
7c6265 |
assert(startPosition < position);
|
|
|
7c6265 |
return text.Substring(startPosition, position-startPosition);
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
string loadName() {
|
|
|
7c6265 |
string name = "";
|
|
|
7c6265 |
loadKey("(");
|
|
|
05068e |
while(text[position] != ')')
|
|
|
7c6265 |
name += text[position++];
|
|
|
05068e |
++position;
|
|
|
7c6265 |
return name.Trim();
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
Test loadTest() {
|
|
|
7c6265 |
Test test = new Test();
|
|
|
7c6265 |
loadKey("{");
|
|
|
7c6265 |
while(tryLoadKey("}") == null) {
|
|
|
7c6265 |
string name = loadFieldName();
|
|
|
7c6265 |
loadKey(":");
|
|
|
7c6265 |
if (name == "name")
|
|
|
7c6265 |
test.name = loadName();
|
|
|
7c6265 |
else
|
|
|
7c6265 |
test.input.Add(name, loadPointListListList());
|
|
|
7c6265 |
}
|
|
|
7c6265 |
return test;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public List<test> loadTestListToEof() {</test>
|
|
|
7c6265 |
List<test> list = new List<test>();</test></test>
|
|
|
7c6265 |
while(true) {
|
|
|
7c6265 |
skipSpaces();
|
|
|
7c6265 |
if (position >= text.Length) break;
|
|
|
7c6265 |
list.Add(loadTest());
|
|
|
7c6265 |
}
|
|
|
7c6265 |
return list;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
};
|
|
|
7c6265 |
|
|
|
7c6265 |
static void loadTests(string text) {
|
|
|
7c6265 |
Loader loader = new Loader();
|
|
|
7c6265 |
loader.text = text;
|
|
|
7c6265 |
tests.Clear();
|
|
|
7c6265 |
tests.AddRange(loader.loadTestListToEof());
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public static void loadTestsFromFile(string filename) {
|
|
|
7c6265 |
loadTests(System.IO.File.ReadAllText(filename));
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public static bool runAll() {
|
|
|
7c6265 |
bool result = true;
|
|
|
7c6265 |
foreach(Test test in tests)
|
|
|
7c6265 |
if (!test.run()) result = false;
|
|
|
7c6265 |
return result;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
static string resultToString(string name, bool result) {
|
|
|
7c6265 |
return name + ": " + (result ? "+" : "FAILED") + "\n";
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public static string makeReport() {
|
|
|
7c6265 |
string report = "";
|
|
|
7c6265 |
foreach(Test test in tests) {
|
|
|
7c6265 |
report += resultToString(test.name, test.result);
|
|
|
7c6265 |
foreach(KeyValuePair<string, bool=""> pair in test.results)</string,>
|
|
|
7c6265 |
report += resultToString(" " + pair.Key, pair.Value);
|
|
|
7c6265 |
}
|
|
|
7c6265 |
return report;
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|
|
|
7c6265 |
public static void saveReport(string filename) {
|
|
|
7c6265 |
System.IO.File.WriteAllText(filename, makeReport());
|
|
|
7c6265 |
}
|
|
|
7c6265 |
}
|
|
|
7c6265 |
}
|
|
|
7c6265 |
|