Blob Blame Raw
/*
    ......... 2015 Ivan Mahonin

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Drawing;

namespace Contours {
    public class Test {
        class Exception: System.Exception { }

        public string name;
        
        public readonly Dictionary<string, List<List<List<Point>>>> input = new Dictionary<string, List<List<List<Point>>>>();
        public readonly Dictionary<string, List<List<List<Point>>>> output = new Dictionary<string, List<List<List<Point>>>>();
        public readonly Dictionary<string, bool> results = new Dictionary<string, bool>();
        public bool result = false;
        
        public static readonly List<Test> tests = new List<Test>();
        
        void check(string name, Shape shape) {
            if (!input.ContainsKey(name)) return;
            List<List<List<Point>>> contours = null;
            try {
                contours = shape.getContours();
            } catch(System.Exception) { }
            output.Add(name, contours);
            results.Add(name, compareContours(input[name], output[name]));
            if (!results[name]) result = false;
        }
        
        Shape tryCreateShape(List<List<List<Point>>> contours) {
            try {
                Shape shape = new Shape();
                shape.setContours(contours);
                return shape;
            } catch (System.Exception) {
                return null;
            }
        }

        Shape tryCombineShapes(Shape.CombinationMode mode, List<List<List<Point>>> a, List<List<List<Point>>> b) {
            try {
                Shape sa = new Shape();
                Shape sb = new Shape();
                sa.setContours(a);
                sb.setContours(b);
                return Shape.combine(mode, sa, sb);
            } catch (System.Exception) {
                return null;
            }
        }
                
        public bool run() {
            result = true;
            
            List<List<List<Point>>> a = null;
            List<List<List<Point>>> b = null;

            if (input.ContainsKey("badA")) a = input["badA"]; else
                if (input.ContainsKey("a")) a = input["a"];
            if (input.ContainsKey("badB")) b = input["badB"]; else
                if (input.ContainsKey("b")) b = input["b"];
            
            if (a != null)
                check("a", tryCreateShape(a));
            if (b != null)
                check("b", tryCreateShape(b));
                
            if (a != null && b != null) {
                check("add", tryCombineShapes(Shape.CombinationMode.Add, a, b));
                check("subtract", tryCombineShapes(Shape.CombinationMode.Subtract, a, b));
                check("intersection", tryCombineShapes(Shape.CombinationMode.Intersection, a, b));
                check("xor", tryCombineShapes(Shape.CombinationMode.Xor, a, b));
            }

            return result;
        }

        public static bool compareContours(List<Point> a, List<Point> b) {
            if (a == null || b == null) return false;
            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<List<Point>> a, List<List<Point>> b) {
            if (a == null || b == null) return false;
            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 = 1; j < b.Count; ++j) {
                    if (!compared[j] && compareContours(a[i], b[j]))
                        { equal = true; compared[j] = true; break; }
                }
                if (!equal) return false;
            }
            return true;
        }

        public static bool compareContours(List<List<List<Point>>> a, List<List<List<Point>>> b) {
            if (a == null || b == null) return false;
            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 = 0; j < b.Count; ++j) {
                    if (!compared[j] && 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;
                if (position < text.Length && text.Substring(position, 2) == "/*") {
                    while(position < text.Length)
                        if (text.Substring(position, 2) == "*/")
                            { position += 2; break; } else ++position;
                    skipSpaces();
                }
            }
            
            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<Point> loadPointList() {
                List<Point> list = new List<Point>();
                loadKey("(");
                if (tryLoadKey(")") == null) do {
                    list.Add(loadPoint());
                } while(loadKey(",", ")") == ",");
                return list;
            }
            
            List<List<Point>> loadPointListList() {
                List<List<Point>> list = new List<List<Point>>();
                loadKey("(");
                if (tryLoadKey(")") == null) do {
                    list.Add(loadPointList());
                } while(loadKey(",", ")") == ",");
                return list;
            }
            
            List<List<List<Point>>> loadPointListListList() {
                List<List<List<Point>>> list = new List<List<List<Point>>>();
                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(text[position] != ')')
                    name += text[position++];
                ++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<Test> loadTestListToEof() {
                List<Test> list = new List<Test>();
                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<string, bool> pair in test.results)
                    report += resultToString("    " + pair.Key, pair.Value);
            }
            return report;
        }
        
        public static void saveReport(string filename) {
            System.IO.File.WriteAllText(filename, makeReport());
        }
    }
}