Blame mono/Contours/Shape.cs

Ivan Mahonin 80bc9b
/*
Ivan Mahonin 80bc9b
    ......... 2015 Ivan Mahonin
Ivan Mahonin 80bc9b
Ivan Mahonin 80bc9b
    This program is free software: you can redistribute it and/or modify
Ivan Mahonin 80bc9b
    it under the terms of the GNU General Public License as published by
Ivan Mahonin 80bc9b
    the Free Software Foundation, either version 3 of the License, or
Ivan Mahonin 80bc9b
    (at your option) any later version.
Ivan Mahonin 80bc9b
Ivan Mahonin 80bc9b
    This program is distributed in the hope that it will be useful,
Ivan Mahonin 80bc9b
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Ivan Mahonin 80bc9b
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Ivan Mahonin 80bc9b
    GNU General Public License for more details.
Ivan Mahonin 80bc9b
Ivan Mahonin 80bc9b
    You should have received a copy of the GNU General Public License
Ivan Mahonin 80bc9b
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
Ivan Mahonin 80bc9b
*/
Ivan Mahonin 80bc9b
Ivan Mahonin d33d2a
using System;
Ivan Mahonin d33d2a
using System.Collections.Generic;
Ivan Mahonin d33d2a
using System.Linq;
Ivan Mahonin f6cc12
using System.Drawing;
Ivan Mahonin d33d2a
Ivan Mahonin d33d2a
namespace Contours {
Ivan Mahonin d33d2a
    public class Shape {
Ivan Mahonin 4717b8
        public class Exception: System.Exception { }
Ivan Mahonin 4717b8
    
Ivan Mahonin 4717b8
        public enum CombinationMode {
Ivan Mahonin 4717b8
            Add,
Ivan Mahonin 4717b8
            Subtract,
Ivan Mahonin 4717b8
            Intersection,
Ivan Mahonin 4717b8
            Xor
Ivan Mahonin 4717b8
        }
Ivan Mahonin 7c6265
    
Ivan Mahonin f6cc12
        class Position {
Ivan Mahonin f6cc12
            public Point point;
Ivan Mahonin 4717b8
            public bool processed = false;
Ivan Mahonin b6d4b6
            public readonly Circuit<Position, Link> output;
Ivan Mahonin b6d4b6
            public readonly Circuit<Position, Link> input;
Ivan Mahonin d33d2a
Ivan Mahonin d33d2a
            public Position() {
Ivan Mahonin b6d4b6
                output = new Circuit<Position, Link>(this);
Ivan Mahonin b6d4b6
                input = new Circuit<Position, Link>(this);
Ivan Mahonin d33d2a
            }
Ivan Mahonin d33d2a
Ivan Mahonin f6cc12
            public Position(Point point): this() { this.point = point; }
Ivan Mahonin d33d2a
        }
Ivan Mahonin d33d2a
Ivan Mahonin f6cc12
        class Contour {
Ivan Mahonin 098941
            public bool inverted = false;
Ivan Mahonin 098941
            
Ivan Mahonin 098941
            public Contour parent;
Ivan Mahonin bb5d1a
            public readonly List<Contour> childs = new List<Contour>();
Ivan Mahonin 4717b8
            public readonly Circuit<Contour, Link> links;
Ivan Mahonin d33d2a
Ivan Mahonin d33d2a
            public Contour() {
Ivan Mahonin 4717b8
                links = new Circuit<Contour, Link>(this);
Ivan Mahonin d33d2a
            }
Ivan Mahonin d33d2a
        }
Ivan Mahonin d33d2a
Ivan Mahonin f6cc12
        class Link {
Ivan Mahonin 4717b8
            public bool alien = false;
Ivan Mahonin 4717b8
            public bool inside = false;
Ivan Mahonin f6cc12
Ivan Mahonin b6d4b6
            public readonly Circuit<Position, Link>.Entry p0;
Ivan Mahonin b6d4b6
            public readonly Circuit<Position, Link>.Entry p1;
Ivan Mahonin d33d2a
            public readonly Circuit<Contour, Link>.Entry contour;
Ivan Mahonin d33d2a
Ivan Mahonin d33d2a
            public Link() {
Ivan Mahonin b6d4b6
                p0 = new Circuit<Position, Link>.Entry(this);
Ivan Mahonin b6d4b6
                p1 = new Circuit<Position, Link>.Entry(this);
Ivan Mahonin d33d2a
                contour = new Circuit<Contour, Link>.Entry(this);
Ivan Mahonin d33d2a
            }
Ivan Mahonin d33d2a
Ivan Mahonin f6cc12
            public Link split(Shape shape, Position position) {
Ivan Mahonin b6d4b6
                if (position.point == p0.getParent().point)
Ivan Mahonin b6d4b6
                    return this;
Ivan Mahonin b6d4b6
                if (position.point == p1.getParent().point)
Ivan Mahonin b6d4b6
                    return p1.getCircuit().getFirst();
Ivan Mahonin 04c11c
                
Ivan Mahonin 04c11c
                shape.log.linkSplitted(this, position);
Ivan Mahonin 04c11c
                
Ivan Mahonin d33d2a
                Link link = new Link();
Ivan Mahonin 4717b8
                link.alien = alien;
Ivan Mahonin b6d4b6
                link.p0.insertBack(position.output);
Ivan Mahonin b6d4b6
                link.p1.insertBack(p1.getCircuit());
Ivan Mahonin b6d4b6
                link.contour.insertAfterOf(contour);
Ivan Mahonin b6d4b6
                
Ivan Mahonin b6d4b6
                p1.insertBack(position.input);
Ivan Mahonin f6cc12
                shape.links.Add(link);
Ivan Mahonin d33d2a
                return link;
Ivan Mahonin d33d2a
            }
Ivan Mahonin d33d2a
Ivan Mahonin f6cc12
            public Link split(Shape shape, Link positionLink) {
Ivan Mahonin b6d4b6
                return split(shape, positionLink.p0.getParent());
Ivan Mahonin b6d4b6
            }
Ivan Mahonin b6d4b6
            
Ivan Mahonin b6d4b6
            public void flip(Shape shape) {
Ivan Mahonin b6d4b6
                shape.log.linkFlipped(this);
Ivan Mahonin b6d4b6
                Position pos0 = p0.getParent();
Ivan Mahonin b6d4b6
                Position pos1 = p1.getParent();
Ivan Mahonin b6d4b6
                p0.insertFront(pos1.output);
Ivan Mahonin b6d4b6
                p1.insertFront(pos0.input);
Ivan Mahonin d33d2a
            }
Ivan Mahonin d33d2a
        }
Ivan Mahonin 04c11c
        
Ivan Mahonin 04c11c
        class Log {
Ivan Mahonin b1e5db
            public readonly Shape shape;
Ivan Mahonin 04c11c
            public bool enabled = true;
Ivan Mahonin 04c11c
            public string filename = "log.txt";
Ivan Mahonin 04c11c
            
Ivan Mahonin b1e5db
            public Log(Shape shape) { this.shape = shape;  }
Ivan Mahonin b1e5db
            
Ivan Mahonin b1e5db
            string toString(Position position) {
Ivan Mahonin b1e5db
                return string.Format(
Ivan Mahonin b1e5db
                    "{0,2}#({1,3}, {2,3})",
Ivan Mahonin b1e5db
                    shape.positions.IndexOf(position),
Ivan Mahonin b1e5db
                    position.point.X,
Ivan Mahonin b1e5db
                    position.point.Y);
Ivan Mahonin 04c11c
            }
Ivan Mahonin 04c11c
            
Ivan Mahonin b1e5db
            string toString(Link link) {
Ivan Mahonin b1e5db
                return string.Format(
Ivan Mahonin 4717b8
                    "{0,2}# {1}{2} {3} {4}",
Ivan Mahonin b1e5db
                    shape.links.IndexOf(link),
Ivan Mahonin 4717b8
                    link.alien ? "B" : "A",
Ivan Mahonin 4717b8
                    link.inside ? "I" : "O",
Ivan Mahonin b6d4b6
                    toString(link.p0.getParent()),
Ivan Mahonin b6d4b6
                    toString(link.p1.getParent()) );
Ivan Mahonin b1e5db
            }
Ivan Mahonin b1e5db
            
Ivan Mahonin b0fc65
            string toString(Contour contour) {
Ivan Mahonin b0fc65
                string s = "";
Ivan Mahonin b0fc65
                for(Link link = contour.links.getFirst(); link != null; link = link.contour.getNextLinear()) {
Ivan Mahonin b0fc65
                    if (s != "") s += ", ";
Ivan Mahonin b6d4b6
                    s += string.Format("({0}, {1})", link.p0.getParent().point.X, link.p0.getParent().point.Y);
Ivan Mahonin b0fc65
                }
Ivan Mahonin b0fc65
                return s;
Ivan Mahonin b0fc65
            }
Ivan Mahonin b0fc65
            
Ivan Mahonin b1e5db
            public void line(string line) {
Ivan Mahonin b1e5db
                if (!enabled) return;
Ivan Mahonin b1e5db
                System.IO.File.AppendAllLines(filename, new string[] { line });
Ivan Mahonin 04c11c
            }
Ivan Mahonin 04c11c
            
Ivan Mahonin 4717b8
            public void linkAdded(Link link) {
Ivan Mahonin 4717b8
                if (!enabled) return;
Ivan Mahonin 4717b8
                line("Link added " + toString(link));
Ivan Mahonin 4717b8
            }
Ivan Mahonin 4717b8
            
Ivan Mahonin 04c11c
            public void linkRemoved(Link link) {
Ivan Mahonin 04c11c
                if (!enabled) return;
Ivan Mahonin b1e5db
                line("Link removed " + toString(link));
Ivan Mahonin 04c11c
            }
Ivan Mahonin 04c11c
            
Ivan Mahonin 04c11c
            public void linkSplitted(Link link, Position position) {
Ivan Mahonin 04c11c
                if (!enabled) return;
Ivan Mahonin b1e5db
                line("Link splitted " + toString(link) + " / " + toString(position));
Ivan Mahonin 04c11c
            }
Ivan Mahonin 04c11c
            
Ivan Mahonin b6d4b6
            public void linkFlipped(Link link) {
Ivan Mahonin b6d4b6
                if (!enabled) return;
Ivan Mahonin b6d4b6
                line("Link flipped " + toString(link));
Ivan Mahonin b6d4b6
            }
Ivan Mahonin b6d4b6
            
Ivan Mahonin 04c11c
            public void linkSwapped(Link a, Link b) {
Ivan Mahonin 04c11c
                if (!enabled) return;
Ivan Mahonin b1e5db
                line("Link swapped " + toString(a) + " <-> " + toString(b));
Ivan Mahonin b1e5db
            }
Ivan Mahonin b1e5db
Ivan Mahonin b1e5db
            public void positionState(Position position) {
Ivan Mahonin b6d4b6
                line("position " + toString(position));
Ivan Mahonin b6d4b6
                for(Link link = position.output.getFirst(); link != null; link = link.p0.getNextLinear())
Ivan Mahonin b6d4b6
                    line("    output " + toString(link));
Ivan Mahonin b6d4b6
                for(Link link = position.input.getFirst(); link != null; link = link.p1.getNextLinear())
Ivan Mahonin b6d4b6
                    line("    input " + toString(link));
Ivan Mahonin b1e5db
            }
Ivan Mahonin b1e5db
Ivan Mahonin b1e5db
            public void state(string title) {
Ivan Mahonin b1e5db
                if (!enabled) return;
Ivan Mahonin b1e5db
                line("-- current state (" + title + ") --");
Ivan Mahonin b1e5db
                line("-- links --");
Ivan Mahonin b1e5db
                foreach(Link link in shape.links)
Ivan Mahonin b1e5db
                    line("    " + toString(link));
Ivan Mahonin b1e5db
                line("-- positions --");
Ivan Mahonin b1e5db
                foreach(Position position in shape.positions)
Ivan Mahonin b1e5db
                    positionState(position);
Ivan Mahonin b0fc65
                line("-- contours --");
Ivan Mahonin b0fc65
                foreach(Contour contour in shape.contours)
Ivan Mahonin b0fc65
                    line("    " + toString(contour));
Ivan Mahonin b1e5db
                line("-- current state end --");
Ivan Mahonin 04c11c
            }
Ivan Mahonin 04c11c
        }
Ivan Mahonin d33d2a
Ivan Mahonin d33d2a
Ivan Mahonin f6cc12
        readonly List<Position> positions = new List<Position>();
Ivan Mahonin f6cc12
        readonly List<Link> links = new List<Link>();
Ivan Mahonin f6cc12
        readonly List<Contour> contours = new List<Contour>();
Ivan Mahonin f6cc12
        readonly List<Contour> rootContours = new List<Contour>();
Ivan Mahonin b1e5db
        readonly Log log;
Ivan Mahonin b1e5db
        
Ivan Mahonin b1e5db
        public Shape() { log = new Log(this); }
Ivan Mahonin f6cc12
Ivan Mahonin f6cc12
        public void clear() {
Ivan Mahonin f6cc12
            positions.Clear();
Ivan Mahonin f6cc12
            links.Clear();
Ivan Mahonin f6cc12
            contours.Clear();
Ivan Mahonin f6cc12
            rootContours.Clear();
Ivan Mahonin d33d2a
        }
Ivan Mahonin d33d2a
Ivan Mahonin f6cc12
        public void setContour(IEnumerable<Point> contour) {
Ivan Mahonin f6cc12
            setContours(new IEnumerable<Point>[] { contour });
Ivan Mahonin d33d2a
        }
Ivan Mahonin d33d2a
Ivan Mahonin 7c6265
        public void setContours(IEnumerable<IEnumerable<IEnumerable<Point>>> contours) {
Ivan Mahonin 7c6265
            List<IEnumerable<Point>> list = new List<IEnumerable<Point>>();
Ivan Mahonin 7c6265
            foreach(IEnumerable<IEnumerable<Point>> c in contours)
Ivan Mahonin 7c6265
                list.AddRange(c);
Ivan Mahonin 7c6265
            setContours(list);
Ivan Mahonin 7c6265
        }
Ivan Mahonin 7c6265
Ivan Mahonin f6cc12
        public void setContours(IEnumerable<IEnumerable<Point>> contours) {
Ivan Mahonin f6cc12
            clear();
Ivan Mahonin 04c11c
            
Ivan Mahonin 04c11c
            log.line("---- setContours begin ----");
Ivan Mahonin 04c11c
            
Ivan Mahonin f6cc12
            foreach(IEnumerable<Point> contour in contours) {
Ivan Mahonin f6cc12
                if (contour.Count() >= 3) {
Ivan Mahonin 4717b8
                    Link first = null;
Ivan Mahonin 4717b8
                    Link previous = null;
Ivan Mahonin f6cc12
                    foreach(Point point in contour) {
Ivan Mahonin f6cc12
                        Position position = new Position(point);
Ivan Mahonin bb5d1a
                        positions.Add(position);
Ivan Mahonin d33d2a
Ivan Mahonin 4717b8
                        Link link = new Link();
Ivan Mahonin b6d4b6
                        link.p0.insertBack(position.output);
Ivan Mahonin 4717b8
                        if (previous != null)
Ivan Mahonin b6d4b6
                            previous.p1.insertBack(link.p0.getParent().input);
Ivan Mahonin 4717b8
                        links.Add(link);
Ivan Mahonin f6cc12
                        
Ivan Mahonin 4717b8
                        if (first == null) first = link;
Ivan Mahonin 4717b8
                        previous = link;
Ivan Mahonin f6cc12
                    }
Ivan Mahonin b6d4b6
                    previous.p1.insertBack(first.p0.getParent().input);
Ivan Mahonin f6cc12
                }
Ivan Mahonin f6cc12
            }
Ivan Mahonin f6cc12
            
Ivan Mahonin e59c4b
            log.state("loaded");
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
            findIntersections();
Ivan Mahonin e59c4b
            log.state("intersections solved");
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
            removeDuplicateLinks();
Ivan Mahonin e59c4b
            log.state("duplicates removed");
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
            fixLinksDirections();
Ivan Mahonin e59c4b
            log.state("directions fixed");
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
            buildContours();
Ivan Mahonin e59c4b
            removeFreeLinks();
Ivan Mahonin e59c4b
            concatenateLinks();
Ivan Mahonin e59c4b
            log.state("links concatenated");
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
            buildContoursHierarhy();
Ivan Mahonin e59c4b
            removeEmptyPositions();
Ivan Mahonin e59c4b
            log.state("setContours complete");
Ivan Mahonin 04c11c
Ivan Mahonin 04c11c
            log.line("---- setContours end ----");
Ivan Mahonin d33d2a
        }
Ivan Mahonin 098941
        
Ivan Mahonin eb0c54
        // returns list of root contour groups
Ivan Mahonin eb0c54
        // first contour in group is outer contour, others - inner
Ivan Mahonin eb0c54
        public List<List<List<Point>>> getContours() {
Ivan Mahonin eb0c54
            List<List<List<Point>>> groups = new List<List<List<Point>>>();
Ivan Mahonin eb0c54
            foreach(Contour root in rootContours) {
Ivan Mahonin eb0c54
                List<List<Point>> list = new List<List<Point>>();
Ivan Mahonin eb0c54
                list.Add(contourToList(root));
Ivan Mahonin eb0c54
                foreach(Contour c in root.childs)
Ivan Mahonin eb0c54
                    list.Add(contourToList(c));
Ivan Mahonin bb5d1a
                groups.Add(list);
Ivan Mahonin 098941
            }
Ivan Mahonin eb0c54
            return groups;
Ivan Mahonin 098941
        }
Ivan Mahonin eb0c54
Ivan Mahonin eb0c54
        List<Point> contourToList(Contour contour) {
Ivan Mahonin eb0c54
            List<Point> list = new List<Point>();
Ivan Mahonin 4717b8
            for(Link link = contour.links.getFirst(); link != null; link = link.contour.getNextLinear())
Ivan Mahonin b6d4b6
                list.Add(link.p0.getParent().point);
Ivan Mahonin eb0c54
            return list;
Ivan Mahonin eb0c54
        }
Ivan Mahonin eb0c54
Ivan Mahonin f6cc12
        void resetTraceInformation() {
Ivan Mahonin f6cc12
            for(int i = 0; i < contours.Count; ++i) {
Ivan Mahonin 4717b8
                contours[i].links.clear();
Ivan Mahonin f6cc12
                contours[i].childs.Clear();
Ivan Mahonin f6cc12
                contours[i].parent = null;
Ivan Mahonin f6cc12
            }
Ivan Mahonin f6cc12
            contours.Clear();
Ivan Mahonin f6cc12
            rootContours.Clear();
Ivan Mahonin f6cc12
        }
Ivan Mahonin f6cc12
        
Ivan Mahonin f6cc12
        void removeLink(Link link) {
Ivan Mahonin 04c11c
            log.linkRemoved(link);
Ivan Mahonin b6d4b6
            link.p0.unlink();
Ivan Mahonin b6d4b6
            link.p1.unlink();
Ivan Mahonin f6cc12
            link.contour.unlink();
Ivan Mahonin f6cc12
            links.Remove(link);
Ivan Mahonin f6cc12
        }
Ivan Mahonin 098941
Ivan Mahonin f6cc12
        void removeEmptyPositions() {
Ivan Mahonin f6cc12
            for(int i = 0; i < positions.Count; ++i)
Ivan Mahonin b6d4b6
                if (positions[i].output.empty() != positions[i].input.empty())
Ivan Mahonin b6d4b6
                    throw new Exception();
Ivan Mahonin b6d4b6
                else
Ivan Mahonin b6d4b6
                if (positions[i].output.empty() && positions[i].input.empty())
Ivan Mahonin f6cc12
                    positions.RemoveAt(i--);
Ivan Mahonin f6cc12
        }
Ivan Mahonin f6cc12
        
Ivan Mahonin f6cc12
        void findIntersections() {
Ivan Mahonin 098941
            bool retry = true;
Ivan Mahonin 098941
            while(retry) {
Ivan Mahonin 098941
                retry = false;
Ivan Mahonin 098941
Ivan Mahonin d33d2a
                // merge positions
Ivan Mahonin f6cc12
                // this procedure may create zero-length links
Ivan Mahonin f6cc12
                for(int i = 0; i < positions.Count; ++i) {
Ivan Mahonin f6cc12
                    for(int j = i+1; j < positions.Count; ++j) {
Ivan Mahonin f6cc12
                        if (positions[i].point == positions[j].point) {
Ivan Mahonin b6d4b6
                            while(positions[j].output.getFirst() != null)
Ivan Mahonin b6d4b6
                                positions[j].output.getFirstEntry().insertBack(positions[i].output);
Ivan Mahonin b6d4b6
                            while(positions[j].input.getFirst() != null)
Ivan Mahonin b6d4b6
                                positions[j].input.getFirstEntry().insertBack(positions[i].input);
Ivan Mahonin f6cc12
                            positions.RemoveAt(j--);
Ivan Mahonin d33d2a
                        }
Ivan Mahonin d33d2a
                    }
Ivan Mahonin d33d2a
                }
Ivan Mahonin f6cc12
                
Ivan Mahonin d33d2a
                // remove zero-length links
Ivan Mahonin f6cc12
                // this procedure may create empty positions
Ivan Mahonin f6cc12
                for(int i = 0; i < links.Count; ++i)
Ivan Mahonin b6d4b6
                    if (links[i].p0.getParent() == links[i].p1.getParent())
Ivan Mahonin f6cc12
                        removeLink(links[i--]);
Ivan Mahonin f6cc12
                
Ivan Mahonin f6cc12
                // so we need to...
Ivan Mahonin f6cc12
                removeEmptyPositions();
Ivan Mahonin f6cc12
                                
Ivan Mahonin d33d2a
                // check intersections
Ivan Mahonin f6cc12
                // this procedure may create new positions, new links and ne intersections
Ivan Mahonin f6cc12
                // so we need to repeat all cycle when intersection found
Ivan Mahonin f6cc12
                for(int i = 0; i < links.Count; ++i) {
Ivan Mahonin f6cc12
                    for(int j = i+1; j < links.Count; ++j) {
Ivan Mahonin f6cc12
                        Link a = links[i];
Ivan Mahonin f6cc12
                        Link b = links[j];
Ivan Mahonin b6d4b6
                        Position a0 = a.p0.getParent();
Ivan Mahonin b6d4b6
                        Position a1 = a.p1.getParent();
Ivan Mahonin b6d4b6
                        Position b0 = b.p0.getParent();
Ivan Mahonin b6d4b6
                        Position b1 = b.p1.getParent();
Ivan Mahonin f6cc12
                        Point c = new Point(0, 0);
Ivan Mahonin f6cc12
                        retry = true; // will reset to false if no intersection
Ivan Mahonin f6cc12
                        
Ivan Mahonin f6cc12
                        switch(Geometry.findIntersection(a0.point, a1.point, b0.point, b1.point, out c))
Ivan Mahonin d33d2a
                        {
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Cross:
Ivan Mahonin f6cc12
                            Position p = new Position(c);
Ivan Mahonin f6cc12
                            positions.Add(p);
Ivan Mahonin f6cc12
                            a.split(this, p);
Ivan Mahonin f6cc12
                            b.split(this, p);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Touch_a0:
Ivan Mahonin f6cc12
                            b.split(this, a0);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Touch_a1:
Ivan Mahonin f6cc12
                            b.split(this, a1);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Touch_b0:
Ivan Mahonin f6cc12
                            a.split(this, b0);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Touch_b1:
Ivan Mahonin f6cc12
                            a.split(this, b1);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_a0_b0_a1_b1:
Ivan Mahonin f6cc12
                            a.split(this, b0);
Ivan Mahonin f6cc12
                            b.split(this, a1);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_a0_b0_b1_a1:
Ivan Mahonin f6cc12
                            a.split(this, b0).split(this, b1);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_a0_b1_a1_b0:
Ivan Mahonin f6cc12
                            a.split(this, b1);
Ivan Mahonin f6cc12
                            b.split(this, a1);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_a0_b1_b0_a1:
Ivan Mahonin f6cc12
                            a.split(this, b1).split(this, b0);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_b0_a0_a1_b1:
Ivan Mahonin f6cc12
                            b.split(this, a0).split(this, a1);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_b0_a0_b1_a1:
Ivan Mahonin f6cc12
                            a.split(this, b1);
Ivan Mahonin f6cc12
                            b.split(this, a0);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_b1_a0_a1_b0:
Ivan Mahonin f6cc12
                            b.split(this, a1).split(this, a0);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin f6cc12
                        case Geometry.IntersectionType.Along_b1_a0_b0_a1:
Ivan Mahonin f6cc12
                            a.split(this, b0);
Ivan Mahonin f6cc12
                            b.split(this, a0);
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin d33d2a
                        default:
Ivan Mahonin d33d2a
                            retry = false;
Ivan Mahonin d33d2a
                            break;
Ivan Mahonin d33d2a
                        }
Ivan Mahonin d33d2a
                    }
Ivan Mahonin d33d2a
                }
Ivan Mahonin d33d2a
            }
Ivan Mahonin d33d2a
        }
Ivan Mahonin d33d2a
        
Ivan Mahonin e59c4b
        void removeDuplicateLinks() {
Ivan Mahonin e59c4b
            foreach(Position position in positions)
Ivan Mahonin e59c4b
                position.processed = false;
Ivan Mahonin e59c4b
            foreach(Position position in positions) {
Ivan Mahonin e59c4b
                position.processed = true;
Ivan Mahonin e59c4b
                Link ol = position.output.getFirst();
Ivan Mahonin e59c4b
                Link il = position.input.getFirst();
Ivan Mahonin e59c4b
                while(ol != null || il != null) {
Ivan Mahonin e59c4b
                    Link linkOut = ol;
Ivan Mahonin e59c4b
                    Link linkIn = il;
Ivan Mahonin e59c4b
                    Position nextPosition = linkOut == null
Ivan Mahonin e59c4b
                                          ? linkIn.p0.getParent()
Ivan Mahonin e59c4b
                                          : linkOut.p1.getParent();
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
                    while(ol != null && ol.p1.getParent() == nextPosition)
Ivan Mahonin e59c4b
                        ol = ol.p0.getNextLinear();
Ivan Mahonin e59c4b
                    while(il != null && il.p0.getParent() == nextPosition)
Ivan Mahonin e59c4b
                        il = il.p1.getNextLinear();
Ivan Mahonin e59c4b
                        
Ivan Mahonin e59c4b
                    if (nextPosition.processed) continue;
Ivan Mahonin e59c4b
                    
Ivan Mahonin e59c4b
                    bool remove = true;
Ivan Mahonin e59c4b
                    Link first = null;
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
                    for(Link lb = linkOut; lb != null;) {
Ivan Mahonin e59c4b
                        Link linkB = lb;
Ivan Mahonin e59c4b
                        lb = lb.p0.getNextLinear();
Ivan Mahonin e59c4b
                        if (linkB.p1.getParent() == nextPosition) {
Ivan Mahonin e59c4b
                            remove = !remove;
Ivan Mahonin e59c4b
                            if (first == null) first = linkB; else removeLink(linkB);
Ivan Mahonin e59c4b
                        }
Ivan Mahonin e59c4b
                    }
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
                    for(Link lb = linkIn; lb != null;) {
Ivan Mahonin e59c4b
                        Link linkB = lb;
Ivan Mahonin e59c4b
                        lb = lb.p1.getNextLinear();
Ivan Mahonin e59c4b
                        if (linkB.p0.getParent() == nextPosition) {
Ivan Mahonin e59c4b
                            remove = !remove;
Ivan Mahonin e59c4b
                            if (first == null) first = linkB; else removeLink(linkB);
Ivan Mahonin e59c4b
                        }
Ivan Mahonin e59c4b
                    }
Ivan Mahonin e59c4b
                    
Ivan Mahonin e59c4b
                    if (remove) removeLink(first);
Ivan Mahonin e59c4b
                }
Ivan Mahonin e59c4b
            }
Ivan Mahonin e59c4b
        }
Ivan Mahonin e59c4b
        
Ivan Mahonin e59c4b
        void fixLinksDirections() {
Ivan Mahonin e59c4b
            log.line("-- fixLinksDirections begin --");
Ivan Mahonin e59c4b
        
Ivan Mahonin e59c4b
            if (positions.Count == 0) return;
Ivan Mahonin e59c4b
            
Ivan Mahonin e59c4b
            foreach(Link linkA in links) {
Ivan Mahonin e59c4b
                Point p0 = linkA.p0.getParent().point;
Ivan Mahonin e59c4b
                Point p1 = linkA.p1.getParent().point;
Ivan Mahonin e59c4b
                Point d = new Point(p1.X - p0.X, p1.Y - p0.Y);
Ivan Mahonin e59c4b
                p1 = new Point(p0.X - d.Y, p0.Y + d.X);
Ivan Mahonin e59c4b
                Geometry.makeLongestLine(p0, ref p1);
Ivan Mahonin e59c4b
                bool inside = true;
Ivan Mahonin e59c4b
                foreach(Link linkB in links) {
Ivan Mahonin e59c4b
                    if (linkA != linkB) {
Ivan Mahonin e59c4b
                        Point pp0 = linkB.p0.getParent().point;
Ivan Mahonin e59c4b
                        Point pp1 = linkB.p1.getParent().point;
Ivan Mahonin e59c4b
                        Point c = new Point(0, 0);
Ivan Mahonin e59c4b
                        switch( Geometry.findIntersection(p0, p1, pp0, pp1, out c) ) {
Ivan Mahonin e59c4b
                            case Geometry.IntersectionType.Cross:
Ivan Mahonin e59c4b
                                inside = !inside;
Ivan Mahonin e59c4b
                                break;
Ivan Mahonin e59c4b
                            case Geometry.IntersectionType.Touch_b0:
Ivan Mahonin e59c4b
                                if ((long)(pp1.X-p0.X)*(long)d.X + (long)(pp1.Y-p0.Y)*(long)d.Y > 0)
Ivan Mahonin e59c4b
                                    inside = !inside;
Ivan Mahonin e59c4b
                                break;
Ivan Mahonin e59c4b
                            case Geometry.IntersectionType.Touch_b1:
Ivan Mahonin e59c4b
                                if ((long)(pp0.X-p0.X)*(long)d.X + (long)(pp0.Y-p0.Y)*(long)d.Y > 0)
Ivan Mahonin e59c4b
                                    inside = !inside;
Ivan Mahonin e59c4b
                                break;
Ivan Mahonin e59c4b
                            case Geometry.IntersectionType.Touch_a0_b0:
Ivan Mahonin e59c4b
                                if ((long)(pp1.X-p0.X)*(long)d.X + (long)(pp1.Y-p0.Y)*(long)d.Y > 0
Ivan Mahonin e59c4b
                                 && (long)(pp1.Y-p0.Y)*(long)d.X - (long)(pp1.X-p0.X)*(long)d.Y > 0)
Ivan Mahonin e59c4b
                                    inside = !inside;
Ivan Mahonin e59c4b
                                break;
Ivan Mahonin e59c4b
                            case Geometry.IntersectionType.Touch_a0_b1:
Ivan Mahonin e59c4b
                                if ((long)(pp0.X-p0.X)*(long)d.X + (long)(pp0.Y-p0.Y)*(long)d.Y > 0
Ivan Mahonin e59c4b
                                 && (long)(pp0.Y-p0.Y)*(long)d.X - (long)(pp0.X-p0.X)*(long)d.Y > 0)
Ivan Mahonin e59c4b
                                    inside = !inside;
Ivan Mahonin e59c4b
                                break;
Ivan Mahonin e59c4b
                            default:
Ivan Mahonin e59c4b
                                break;
Ivan Mahonin e59c4b
                        }
Ivan Mahonin e59c4b
                    }
Ivan Mahonin e59c4b
                }
Ivan Mahonin e59c4b
                if (inside) linkA.flip(this);
Ivan Mahonin e59c4b
            }
Ivan Mahonin e59c4b
Ivan Mahonin e59c4b
            log.line("-- fixLinksDirections end --");
Ivan Mahonin b6d4b6
        }
Ivan Mahonin b6d4b6
        
Ivan Mahonin 4717b8
        void findLinksInside() {
Ivan Mahonin 4717b8
            for(int i = 0; i < links.Count; ++i) {
Ivan Mahonin 4717b8
                int intersectionsCount = 0;
Ivan Mahonin b6d4b6
                Point p0 = links[i].p0.getParent().point;
Ivan Mahonin b6d4b6
                Point p1 = links[i].p1.getParent().point;
Ivan Mahonin 4717b8
                Geometry.makeLongestLine(p0, ref p1);
Ivan Mahonin 4717b8
                for(int j = 0; j < links.Count; ++j) {
Ivan Mahonin 4717b8
                    if (i != j && links[i].alien != links[j].alien) {
Ivan Mahonin 4717b8
                        Point c = new Point(0, 0);
Ivan Mahonin b6d4b6
                        Point pp0 = links[j].p0.getParent().point;
Ivan Mahonin b6d4b6
                        Point pp1 = links[j].p1.getParent().point;
Ivan Mahonin 4717b8
                        int d = Math.Sign( (long)(p0.Y - p1.Y)*(long)(pp1.X - pp0.X)
Ivan Mahonin 4717b8
                                         + (long)(p1.X - p0.X)*(long)(pp1.Y - pp0.Y) );
Ivan Mahonin 4717b8
                        switch(Geometry.findIntersection(p0, p1, pp0, pp1, out c)) {
Ivan Mahonin 4717b8
                            case Geometry.IntersectionType.Cross:
Ivan Mahonin 4717b8
                                intersectionsCount += 2*d;
Ivan Mahonin 4717b8
                                break;
Ivan Mahonin 4717b8
                            case Geometry.IntersectionType.Touch_b0:
Ivan Mahonin 4717b8
                            case Geometry.IntersectionType.Touch_b1:
Ivan Mahonin 4717b8
                                intersectionsCount += d;
Ivan Mahonin 4717b8
                                break;
Ivan Mahonin 4717b8
                            default:
Ivan Mahonin 4717b8
                                break;
Ivan Mahonin 4717b8
                        }
Ivan Mahonin e5b120
                    }
Ivan Mahonin e5b120
                }
Ivan Mahonin 4717b8
                links[i].inside = intersectionsCount == 2;
Ivan Mahonin e5b120
            }
Ivan Mahonin e5b120
        }
Ivan Mahonin e5b120
        
Ivan Mahonin 4717b8
        bool combine(CombinationMode mode, bool self, bool alien) {
Ivan Mahonin 4717b8
            switch(mode) {
Ivan Mahonin 4717b8
                case CombinationMode.Add: return self || alien;
Ivan Mahonin 4717b8
                case CombinationMode.Subtract: return self && !alien;
Ivan Mahonin 4717b8
                case CombinationMode.Intersection: return self && alien;
Ivan Mahonin 4717b8
                case CombinationMode.Xor: return self != alien;
Ivan Mahonin 4717b8
                default: break;
Ivan Mahonin 4717b8
            }
Ivan Mahonin 4717b8
            return false;
Ivan Mahonin 4717b8
        }
Ivan Mahonin 4717b8
        
Ivan Mahonin 4717b8
        void removeUnusedLinks(CombinationMode mode) {
Ivan Mahonin 4717b8
            foreach(Position position in positions)
Ivan Mahonin 4717b8
                position.processed = false;
Ivan Mahonin f6cc12
            foreach(Position position in positions) {
Ivan Mahonin 4717b8
                position.processed = true;
Ivan Mahonin b6d4b6
                Link ol = position.output.getFirst();
Ivan Mahonin b6d4b6
                Link il = position.input.getFirst();
Ivan Mahonin b6d4b6
                while(ol != null || il != null) {
Ivan Mahonin b6d4b6
                    Link linkOut = ol;
Ivan Mahonin b6d4b6
                    Link linkIn = il;
Ivan Mahonin b6d4b6
                    Position nextPosition = linkOut == null
Ivan Mahonin b6d4b6
                                          ? linkIn.p0.getParent()
Ivan Mahonin b6d4b6
                                          : linkOut.p1.getParent();
Ivan Mahonin b6d4b6
Ivan Mahonin b6d4b6
                    while(ol != null && ol.p1.getParent() == nextPosition)
Ivan Mahonin b6d4b6
                        ol = ol.p0.getNextLinear();
Ivan Mahonin b6d4b6
                    while(il != null && il.p0.getParent() == nextPosition)
Ivan Mahonin b6d4b6
                        il = il.p1.getNextLinear();
Ivan Mahonin b6d4b6
                        
Ivan Mahonin b6d4b6
                    if (nextPosition.processed) continue;
Ivan Mahonin 4717b8
                    
Ivan Mahonin 4717b8
                    bool forwardSelf = false;
Ivan Mahonin 4717b8
                    bool forwardAlien = false;
Ivan Mahonin f6cc12
                    Link forwardLink = null;
Ivan Mahonin 4717b8
                    bool backwardSelf = false;
Ivan Mahonin 4717b8
                    bool backwardAlien = false;
Ivan Mahonin f6cc12
                    Link backwardLink = null;
Ivan Mahonin 4717b8
Ivan Mahonin b6d4b6
                    for(Link lb = linkOut; lb != null;) {
Ivan Mahonin 4717b8
                        Link linkB = lb;
Ivan Mahonin b6d4b6
                        lb = lb.p0.getNextLinear();
Ivan Mahonin b6d4b6
                        if (linkB.p1.getParent() == nextPosition) {
Ivan Mahonin 4717b8
                            if (linkB.alien) {
Ivan Mahonin 4717b8
                                forwardAlien = true;
Ivan Mahonin 4717b8
                                if (linkB.inside) forwardSelf = backwardSelf = true;
Ivan Mahonin 4717b8
                            } else {
Ivan Mahonin 4717b8
                                forwardSelf = true;
Ivan Mahonin 4717b8
                                if (linkB.inside) forwardAlien = backwardAlien = true;
Ivan Mahonin 4717b8
                            }
Ivan Mahonin 4717b8
                            if (forwardLink == null) forwardLink = linkB; else removeLink(linkB);
Ivan Mahonin f6cc12
                        }
Ivan Mahonin e5b120
                    }
Ivan Mahonin f6cc12
Ivan Mahonin b6d4b6
                    for(Link lb = linkIn; lb != null;) {
Ivan Mahonin b6d4b6
                        Link linkB = lb;
Ivan Mahonin b6d4b6
                        lb = lb.p1.getNextLinear();
Ivan Mahonin b6d4b6
                        if (linkB.p0.getParent() == nextPosition) {
Ivan Mahonin b6d4b6
                            if (linkB.alien) {
Ivan Mahonin b6d4b6
                                backwardAlien = true;
Ivan Mahonin b6d4b6
                                if (linkB.inside) forwardSelf = backwardSelf = true;
Ivan Mahonin b6d4b6
                            } else {
Ivan Mahonin b6d4b6
                                backwardSelf = true;
Ivan Mahonin b6d4b6
                                if (linkB.inside) forwardAlien = backwardAlien = true;
Ivan Mahonin 4717b8
                            }
Ivan Mahonin b6d4b6
                            if (backwardLink == null) backwardLink = linkB; else removeLink(linkB);
Ivan Mahonin f6cc12
                        }
Ivan Mahonin 4717b8
                    }
Ivan Mahonin f6cc12
                    
Ivan Mahonin 4717b8
                    bool forward = combine(mode, forwardSelf, forwardAlien);
Ivan Mahonin 4717b8
                    bool backward = combine(mode, backwardSelf, backwardAlien);
Ivan Mahonin b6d4b6
                    if (forwardLink == null && backwardLink == null)
Ivan Mahonin b6d4b6
                        throw new Exception();
Ivan Mahonin b6d4b6
Ivan Mahonin b6d4b6
                    if (forward && !backward) {
Ivan Mahonin b6d4b6
                        if (forwardLink == null) backwardLink.flip(this);
Ivan Mahonin b6d4b6
                            else if (backwardLink != null) removeLink(backwardLink);
Ivan Mahonin b6d4b6
                    } else
Ivan Mahonin b6d4b6
                    if (!forward && backward) {
Ivan Mahonin b6d4b6
                        if (backwardLink == null) forwardLink.flip(this);
Ivan Mahonin b6d4b6
                            else if (forwardLink != null) removeLink(forwardLink);
Ivan Mahonin b6d4b6
                    } else {
Ivan Mahonin b6d4b6
                        if (forwardLink != null) removeLink(forwardLink);
Ivan Mahonin b6d4b6
                        if (backwardLink != null) removeLink(backwardLink);
Ivan Mahonin 4717b8
                    }
Ivan Mahonin e5b120
                }
Ivan Mahonin e5b120
            }
Ivan Mahonin e5b120
        }
Ivan Mahonin e5b120
        
Ivan Mahonin f6cc12
        void buildContours() {
Ivan Mahonin f6cc12
            for(int i = 0; i < links.Count; ++i) {
Ivan Mahonin 4717b8
                if (links[i].contour.getParent() == null) {
Ivan Mahonin e5b120
                    Contour contour = new Contour();
Ivan Mahonin f6cc12
                    contours.Add(contour);
Ivan Mahonin 098941
                    
Ivan Mahonin 4717b8
                    Link link = links[i];
Ivan Mahonin 098941
                    do {
Ivan Mahonin 4717b8
                        if (link.contour.getParent() != null)
Ivan Mahonin 098941
                            throw new Exception();
Ivan Mahonin 4717b8
                        link.contour.insertBack(contour.links);
Ivan Mahonin 098941
                        
Ivan Mahonin 4717b8
                        // find first link CW order (so we turns left)
Ivan Mahonin b6d4b6
                        Link nextLink = link.p1.getParent().output.getFirst();
Ivan Mahonin b6d4b6
                        for(Link l = nextLink.p0.getNext(); l != null; l = l.p0.getNextLinear())
Ivan Mahonin 4717b8
                            if ( Geometry.isCCW(
Ivan Mahonin b6d4b6
                                    link.p1.getParent().point,
Ivan Mahonin b6d4b6
                                    link.p0.getParent().point,
Ivan Mahonin b6d4b6
                                    nextLink.p1.getParent().point,
Ivan Mahonin b6d4b6
                                    l.p1.getParent().point ))
Ivan Mahonin 4717b8
                                { nextLink = l; }
Ivan Mahonin 098941
                        
Ivan Mahonin f6cc12
                        // select first link by the left (links should be CCW-ordered)
Ivan Mahonin 4717b8
                        link = nextLink;
Ivan Mahonin 4717b8
                    } while (link != links[i]);
Ivan Mahonin 098941
                }
Ivan Mahonin 098941
            }
Ivan Mahonin 098941
        }
Ivan Mahonin 098941
Ivan Mahonin b0fc65
        void concatenateLinks() {
Ivan Mahonin b0fc65
            foreach(Contour contour in contours) {
Ivan Mahonin b0fc65
                for(Link linkA = contour.links.getFirst(); linkA != null;) {
Ivan Mahonin b0fc65
                    Link linkB = linkA.contour.getNext();
Ivan Mahonin b0fc65
                    if ( Geometry.isPointAtLine(
Ivan Mahonin b6d4b6
                            linkA.p1.getParent().point,
Ivan Mahonin b6d4b6
                            linkA.p0.getParent().point,
Ivan Mahonin b6d4b6
                            linkB.p1.getParent().point ))
Ivan Mahonin b0fc65
                    {
Ivan Mahonin b6d4b6
                        linkA.p1.insertAfterOf(linkB.p1);
Ivan Mahonin b0fc65
                        removeLink(linkB);
Ivan Mahonin b0fc65
                        if (linkA == linkB) break;
Ivan Mahonin b0fc65
                    } else {
Ivan Mahonin b0fc65
                        linkA = linkA.contour.getNextLinear();
Ivan Mahonin b0fc65
                    }
Ivan Mahonin b0fc65
                }
Ivan Mahonin b0fc65
            }
Ivan Mahonin b0fc65
        }
Ivan Mahonin b0fc65
Ivan Mahonin f6cc12
        // Normally we should not have free links after tracing
Ivan Mahonin 098941
        void removeFreeLinks() {
Ivan Mahonin f6cc12
            for(int i = 0; i < links.Count; ++i)
Ivan Mahonin f6cc12
                if (links[i].contour.getParent() == null)
Ivan Mahonin 367b01
                    throw new Exception();
Ivan Mahonin 098941
        }
Ivan Mahonin 75fa8e
Ivan Mahonin f6cc12
        int countIntersectionsWithContour(Point p0, Point p1, Contour contour) {
Ivan Mahonin 098941
            int count = 0;
Ivan Mahonin 4717b8
            for(Link link = contour.links.getFirst(); link != null; link = link.contour.getNextLinear()) {
Ivan Mahonin b6d4b6
                Point pp0 = link.p0.getParent().point;
Ivan Mahonin b6d4b6
                Point pp1 = link.contour.getNext().p0.getParent().point;
Ivan Mahonin f6cc12
                int d = Math.Sign( (long)(p0.Y - p1.Y)*(long)(pp1.X - pp0.X)
Ivan Mahonin f6cc12
                                 + (long)(p1.X - p0.X)*(long)(pp1.Y - pp0.Y) );
Ivan Mahonin f6cc12
                Point c;
Ivan Mahonin f6cc12
                switch(Geometry.findIntersection(p0, p1, pp0, pp1, out c)) {
Ivan Mahonin f6cc12
                case Geometry.IntersectionType.Cross:
Ivan Mahonin 098941
                    count += 2*d;
Ivan Mahonin 098941
                    break;
Ivan Mahonin f6cc12
                case Geometry.IntersectionType.Touch_b0:
Ivan Mahonin f6cc12
                case Geometry.IntersectionType.Touch_b1:
Ivan Mahonin 4717b8
                    count += d;
Ivan Mahonin 098941
                    break;
Ivan Mahonin 098941
                default:
Ivan Mahonin 098941
                    break;
Ivan Mahonin e5b120
                }
Ivan Mahonin e5b120
            }
Ivan Mahonin 098941
            return count;
Ivan Mahonin 098941
        }
Ivan Mahonin 098941
Ivan Mahonin 098941
        bool isContourInverted(Contour contour) {
Ivan Mahonin 4717b8
            Link first = contour.links.getFirst();
Ivan Mahonin b6d4b6
            Point p0 = first.p0.getParent().point;
Ivan Mahonin b6d4b6
            Point p1 = first.contour.getNext().p0.getParent().point;
Ivan Mahonin f6cc12
            Geometry.makeLongestLine(p0, ref p1);
Ivan Mahonin f6cc12
            return countIntersectionsWithContour(p0, p1, contour) < 0;
Ivan Mahonin 098941
        }
Ivan Mahonin 098941
        
Ivan Mahonin 098941
        bool isContourInside(Contour inner, Contour outer) {
Ivan Mahonin 4717b8
            Link first = inner.links.getFirst();
Ivan Mahonin b6d4b6
            Point p0 = first.p0.getParent().point;
Ivan Mahonin b6d4b6
            Point p1 = first.contour.getNext().p0.getParent().point;
Ivan Mahonin f6cc12
            Geometry.makeLongestLine(p0, ref p1);
Ivan Mahonin f6cc12
            return countIntersectionsWithContour(p0, p1, outer) != 0;
Ivan Mahonin 098941
        }
Ivan Mahonin 098941
        
Ivan Mahonin f6cc12
        void organizeChildContours(Contour contour, Contour parent) {
Ivan Mahonin 098941
            // set parent
Ivan Mahonin 098941
            contour.parent = parent;
Ivan Mahonin 098941
Ivan Mahonin 098941
            // remove sub-childs from parent list
Ivan Mahonin 098941
            if (parent != null)
Ivan Mahonin 098941
                foreach(Contour c in contour.childs)
Ivan Mahonin 098941
                    parent.childs.Remove(c);
Ivan Mahonin 098941
Ivan Mahonin 098941
            // sub-calls
Ivan Mahonin 098941
            foreach(Contour c in contour.childs)
Ivan Mahonin f6cc12
                organizeChildContours(c, contour);
Ivan Mahonin 098941
        }
Ivan Mahonin 098941
        
Ivan Mahonin 4717b8
        void buildContoursHierarhy() {
Ivan Mahonin 098941
            // calculate directions of contours
Ivan Mahonin f6cc12
            foreach(Contour contour in contours)
Ivan Mahonin f6cc12
                contour.inverted = isContourInverted(contour);
Ivan Mahonin 098941
                
Ivan Mahonin 098941
            // find childs
Ivan Mahonin f6cc12
            rootContours.AddRange(contours);
Ivan Mahonin f6cc12
            for(int i = 0; i < contours.Count; ++i) {
Ivan Mahonin b0fc65
                for(int j = i+1; j < contours.Count; ++j) {
Ivan Mahonin b0fc65
                    if (isContourInside(contours[i], contours[j])) {
Ivan Mahonin b0fc65
                        contours[j].childs.Add(contours[i]);
Ivan Mahonin b0fc65
                        rootContours.Remove(contours[i]);
Ivan Mahonin b0fc65
                    } else
Ivan Mahonin b0fc65
                    if (isContourInside(contours[j], contours[i])) {
Ivan Mahonin b0fc65
                        contours[i].childs.Add(contours[j]);
Ivan Mahonin b0fc65
                        rootContours.Remove(contours[j]);
Ivan Mahonin 098941
                    }
Ivan Mahonin 367b01
                }
Ivan Mahonin 098941
            }
Ivan Mahonin 098941
            
Ivan Mahonin f6cc12
            // organize childs
Ivan Mahonin 098941
            foreach(Contour c in rootContours)
Ivan Mahonin f6cc12
                organizeChildContours(c, null);
Ivan Mahonin f6cc12
           
Ivan Mahonin 367b01
            // remove invisible contours
Ivan Mahonin 4717b8
            /*
Ivan Mahonin f6cc12
            for(int i = 0; i < contours.Count; ++i) {
Ivan Mahonin f6cc12
                bool parentInverted = contours[i].parent == null || contours[i].parent.inverted;
Ivan Mahonin f6cc12
                if (parentInverted == contours[i].inverted) {
Ivan Mahonin 4717b8
                    // remove contour
Ivan Mahonin 4717b8
                    foreach(Contour c in contours[i].childs)
Ivan Mahonin 4717b8
                        c.parent = contours[i].parent;
Ivan Mahonin 4717b8
                    List<Contour> parentList = contours[i].parent == null
Ivan Mahonin 4717b8
                                             ? rootContours
Ivan Mahonin 4717b8
                                             : contours[i].parent.childs;
Ivan Mahonin 4717b8
                    parentList.AddRange(contours[i].childs);
Ivan Mahonin 4717b8
                    
Ivan Mahonin 4717b8
                    contours[i].parent = null;
Ivan Mahonin 4717b8
                    contours[i].childs.Clear();
Ivan Mahonin 4717b8
                    while(!contours[i].links.empty())
Ivan Mahonin 4717b8
                        removeLink(contours[i].links.getFirst());
Ivan Mahonin 4717b8
                    
Ivan Mahonin 4717b8
                    contours.RemoveAt(i--);
Ivan Mahonin 367b01
                }
Ivan Mahonin 367b01
            }
Ivan Mahonin 4717b8
            */
Ivan Mahonin e5b120
Ivan Mahonin 367b01
            // move contours in the holes to root
Ivan Mahonin 367b01
            for(int i = 0; i < rootContours.Count; ++i) {
Ivan Mahonin 367b01
                Contour contourA = rootContours[i];
Ivan Mahonin 367b01
                foreach(Contour contourB in contourA.childs) {
Ivan Mahonin 367b01
                    if (contourB.childs.Count != 0) {
Ivan Mahonin 367b01
                        foreach(Contour c in contourB.childs)
Ivan Mahonin 367b01
                            c.parent = null;
Ivan Mahonin 367b01
                        rootContours.AddRange(contourB.childs);
Ivan Mahonin 367b01
                    }
Ivan Mahonin 367b01
                }
Ivan Mahonin 367b01
            }
Ivan Mahonin e5b120
        }
Ivan Mahonin 367b01
Ivan Mahonin 4717b8
        void calculate(CombinationMode combinationMode) {
Ivan Mahonin b1e5db
            log.state("calculation begin");
Ivan Mahonin b1e5db
        
Ivan Mahonin f6cc12
            resetTraceInformation();
Ivan Mahonin e5b120
            findIntersections();
Ivan Mahonin b1e5db
            log.state("intersections solved");
Ivan Mahonin b1e5db
Ivan Mahonin 4717b8
            findLinksInside();
Ivan Mahonin 4717b8
            log.state("inside found");
Ivan Mahonin 4717b8
Ivan Mahonin 4717b8
            removeUnusedLinks(combinationMode);
Ivan Mahonin 4717b8
            log.state("unused links removed");
Ivan Mahonin b1e5db
Ivan Mahonin f6cc12
            buildContours();
Ivan Mahonin f6cc12
            removeFreeLinks();
Ivan Mahonin b0fc65
            concatenateLinks();
Ivan Mahonin b0fc65
            log.state("links concatenated");
Ivan Mahonin b0fc65
Ivan Mahonin 4717b8
            buildContoursHierarhy();
Ivan Mahonin 098941
            removeEmptyPositions();
Ivan Mahonin b1e5db
Ivan Mahonin b1e5db
            log.state("calculation complete");
Ivan Mahonin d33d2a
        }
Ivan Mahonin eb0c54
        
Ivan Mahonin 4717b8
        public static Shape combine(CombinationMode mode, Shape a, Shape b) {
Ivan Mahonin eb0c54
            Shape sum = new Shape();
Ivan Mahonin 04c11c
            sum.log.enabled = a.log.enabled && b.log.enabled;
Ivan Mahonin 04c11c
Ivan Mahonin 4717b8
            sum.log.line(string.Format("---- combine {0} begin ----", mode));
Ivan Mahonin eb0c54
Ivan Mahonin eb0c54
            // clone a
Ivan Mahonin eb0c54
            for(int i = 0; i < a.positions.Count; ++i)
Ivan Mahonin eb0c54
                sum.positions.Add(new Position(a.positions[i].point));
Ivan Mahonin eb0c54
            for(int i = 0; i < a.positions.Count; ++i) {
Ivan Mahonin b6d4b6
                for(Link link = a.positions[i].output.getFirst(); link != null; link = link.p0.getNextLinear()) {
Ivan Mahonin eb0c54
                    Link l = new Link();
Ivan Mahonin 4717b8
                    l.alien = false;
Ivan Mahonin b6d4b6
                    l.p0.insertBack(sum.positions[i].output);
Ivan Mahonin b6d4b6
                    l.p1.insertBack(sum.positions[a.positions.IndexOf(link.p1.getParent())].input);
Ivan Mahonin eb0c54
                    sum.links.Add(l);
Ivan Mahonin eb0c54
                }
Ivan Mahonin eb0c54
            }
Ivan Mahonin eb0c54
             
Ivan Mahonin eb0c54
            // clone b
Ivan Mahonin eb0c54
            for(int i = 0; i < b.positions.Count; ++i)
Ivan Mahonin eb0c54
                sum.positions.Add(new Position(b.positions[i].point));
Ivan Mahonin eb0c54
            for(int i = 0; i < b.positions.Count; ++i) {
Ivan Mahonin b6d4b6
                for(Link link = b.positions[i].output.getFirst(); link != null; link = link.p0.getNextLinear()) {
Ivan Mahonin eb0c54
                    Link l = new Link();
Ivan Mahonin 4717b8
                    l.alien = true;
Ivan Mahonin b6d4b6
                    l.p0.insertBack(sum.positions[a.positions.Count + i].output);
Ivan Mahonin b6d4b6
                    l.p1.insertBack(sum.positions[a.positions.Count + b.positions.IndexOf(link.p1.getParent())].input);
Ivan Mahonin bb5d1a
                    sum.links.Add(l);
Ivan Mahonin eb0c54
                }
Ivan Mahonin eb0c54
            }
Ivan Mahonin eb0c54
            
Ivan Mahonin 4717b8
            sum.calculate(mode);
Ivan Mahonin 04c11c
Ivan Mahonin 4717b8
            sum.log.line("---- combine end ----");
Ivan Mahonin 04c11c
Ivan Mahonin eb0c54
            return sum;
Ivan Mahonin eb0c54
        }
Ivan Mahonin eb0c54
        
Ivan Mahonin 4717b8
        public static Shape add(Shape a, Shape b)
Ivan Mahonin 4717b8
            { return combine(CombinationMode.Add, a, b); }
Ivan Mahonin eb0c54
Ivan Mahonin 4717b8
        public static Shape subtract(Shape a, Shape b)
Ivan Mahonin 4717b8
            { return combine(CombinationMode.Subtract, a, b); }
Ivan Mahonin eb0c54
Ivan Mahonin 4717b8
        public static Shape intersection(Shape a, Shape b)
Ivan Mahonin 4717b8
            { return combine(CombinationMode.Intersection, a, b); }
Ivan Mahonin eb0c54
Ivan Mahonin 4717b8
        public static Shape xor(Shape a, Shape b)
Ivan Mahonin 4717b8
            { return combine(CombinationMode.Xor, a, b); }
Ivan Mahonin d33d2a
    }
Ivan Mahonin d33d2a
}
Ivan Mahonin d33d2a