Blame mono/Contours/Shape.cs

d33d2a
using System;
d33d2a
using System.Collections.Generic;
d33d2a
using System.Linq;
f6cc12
using System.Drawing;
d33d2a
d33d2a
namespace Contours {
d33d2a
    public class Shape {
7c6265
        class Exception: System.Exception { }
7c6265
    
f6cc12
        class Position {
f6cc12
            public Point point;
d33d2a
            public readonly Circuit<position, link=""> links;</position,>
d33d2a
d33d2a
            public Position() {
d33d2a
                links = new Circuit<position, link="">(this);</position,>
d33d2a
            }
d33d2a
f6cc12
            public Position(Point point): this() { this.point = point; }
d33d2a
        }
d33d2a
f6cc12
        class Contour {
098941
            public bool inverted = false;
098941
            
098941
            public Contour parent;
bb5d1a
            public readonly List<contour> childs = new List<contour>();</contour></contour>
098941
d33d2a
            public readonly Circuit<contour, link=""> forward;</contour,>
d33d2a
            public readonly Circuit<contour, link=""> backward;</contour,>
d33d2a
d33d2a
            public Contour() {
d33d2a
                forward = new Circuit<contour, link="">(this);</contour,>
d33d2a
                backward = new Circuit<contour, link="">(this);</contour,>
d33d2a
            }
d33d2a
        }
d33d2a
f6cc12
        class Link {
f6cc12
            public bool forward = false;
f6cc12
            public Position nextPosition = null;
f6cc12
d33d2a
            public readonly Circuit<position, link="">.Entry position;</position,>
d33d2a
            public readonly Circuit<contour, link="">.Entry contour;</contour,>
d33d2a
d33d2a
            public Link() {
d33d2a
                position = new Circuit<position, link="">.Entry(this);</position,>
d33d2a
                contour = new Circuit<contour, link="">.Entry(this);</contour,>
d33d2a
            }
d33d2a
f6cc12
            public Link split(Shape shape, Position position) {
04c11c
                if (position.point == this.position.getParent().point) return this;
04c11c
                if (position.point == nextPosition.point) return nextPosition.links.getFirst();
04c11c
                
04c11c
                shape.log.linkSplitted(this, position);
04c11c
                
d33d2a
                Link link = new Link();
d33d2a
                link.position.insertBack(position.links);
d33d2a
                link.contour.insertAfterOf(contour);
f6cc12
                link.forward = forward;
f6cc12
                link.nextPosition = nextPosition;
f6cc12
                nextPosition = position;
f6cc12
                shape.links.Add(link);
d33d2a
                return link;
d33d2a
            }
d33d2a
f6cc12
            public Link split(Shape shape, Link positionLink) {
f6cc12
                return split(shape, positionLink.position.getParent());
d33d2a
            }
d33d2a
        }
04c11c
        
04c11c
        class Log {
04c11c
            public bool enabled = true;
04c11c
            public string filename = "log.txt";
04c11c
            
04c11c
            public void line(string line) {
04c11c
                if (!enabled) return;
04c11c
                System.IO.File.AppendAllLines(filename, new string[] { line });
04c11c
            }
04c11c
            
04c11c
            public void linkCreated(Link link) {
04c11c
                if (!enabled) return;
04c11c
                line(string.Format(
04c11c
                    "Link created {0} {1} {2}",
04c11c
                    link.forward ? "->" : "<-",
04c11c
                    link.position.getParent().point,
04c11c
                    link.nextPosition.point ));
04c11c
            }
04c11c
            
04c11c
            public void linkRemoved(Link link) {
04c11c
                if (!enabled) return;
04c11c
                line(string.Format(
04c11c
                    "Link removed {0} {1} {2}",
04c11c
                    link.forward ? "->" : "<-",
04c11c
                    link.position.getParent().point,
04c11c
                    link.nextPosition.point ));
04c11c
            }
04c11c
            
04c11c
            public void linkSplitted(Link link, Position position) {
04c11c
                if (!enabled) return;
04c11c
                line(string.Format(
04c11c
                    "Link splitted {0} {1}/{3}/{2}",
04c11c
                    link.forward ? "->" : "<-",
04c11c
                    link.position.getParent().point,
04c11c
                    link.nextPosition.point,
04c11c
                    position.point ));
04c11c
            }
04c11c
            
04c11c
            public void linkSwapped(Link a, Link b) {
04c11c
                if (!enabled) return;
04c11c
                line(string.Format(
04c11c
                    "Link swapped {0} {1} {2} - {3} {4} {5}",
04c11c
                    a.forward ? "->" : "<-",
04c11c
                    a.position.getParent().point,
04c11c
                    a.nextPosition.point,
04c11c
                    b.forward ? "->" : "<-",
04c11c
                    b.position.getParent().point,
04c11c
                    b.nextPosition.point ));
04c11c
            }
04c11c
        }
d33d2a
d33d2a
f6cc12
        readonly List<position> positions = new List<position>();</position></position>
f6cc12
        readonly List<link> links = new List<link>();
f6cc12
        readonly List<contour> contours = new List<contour>();</contour></contour>
f6cc12
        readonly List<contour> rootContours = new List<contour>();</contour></contour>
04c11c
        readonly Log log = new Log();
f6cc12
f6cc12
        public void clear() {
f6cc12
            positions.Clear();
f6cc12
            links.Clear();
f6cc12
            contours.Clear();
f6cc12
            rootContours.Clear();
d33d2a
        }
d33d2a
f6cc12
        public void setContour(IEnumerable<point> contour) {</point>
f6cc12
            setContours(new IEnumerable<point>[] { contour });</point>
d33d2a
        }
d33d2a
7c6265
        public void setContours(IEnumerable<ienumerable<ienumerable<point>>> contours) {</ienumerable<ienumerable<point>
7c6265
            List<ienumerable<point>> list = new List<ienumerable<point>>();</ienumerable<point></ienumerable<point>
7c6265
            foreach(IEnumerable<ienumerable<point>> c in contours)</ienumerable<point>
7c6265
                list.AddRange(c);
7c6265
            setContours(list);
7c6265
        }
7c6265
f6cc12
        public void setContours(IEnumerable<ienumerable<point>> contours) {</ienumerable<point>
f6cc12
            clear();
04c11c
            
04c11c
            log.line("---- setContours begin ----");
04c11c
            
f6cc12
            foreach(IEnumerable<point> contour in contours) {</point>
f6cc12
                if (contour.Count() >= 3) {
f6cc12
                    Link firstForward = null;
f6cc12
                    Link firstBackward = null;
f6cc12
                    Link previousForward = null;
f6cc12
                    Link previousBackward = null;
f6cc12
                    foreach(Point point in contour) {
f6cc12
                        Position position = new Position(point);
bb5d1a
                        positions.Add(position);
d33d2a
f6cc12
                        Link forward = new Link();
f6cc12
                        forward.forward = true;
f6cc12
                        forward.position.insertBack(position.links);
f6cc12
                        if (previousForward != null)
f6cc12
                            previousForward.nextPosition = forward.position.getParent();
f6cc12
                        links.Add(forward);
f6cc12
                        
f6cc12
                        Link backward = new Link();
f6cc12
                        backward.forward = false;
f6cc12
                        backward.position.insertBack(position.links);
f6cc12
                        if (previousBackward != null)
f6cc12
                            backward.nextPosition = previousBackward.position.getParent();
f6cc12
                        links.Add(backward);
f6cc12
                        
f6cc12
                        if (firstForward == null) firstForward = forward;
f6cc12
                        if (firstBackward == null) firstBackward = backward;
f6cc12
                        previousForward = forward;
f6cc12
                        previousBackward = backward;
f6cc12
                    }
f6cc12
                    previousForward.nextPosition = firstForward.position.getParent();
f6cc12
                    firstBackward.nextPosition = previousBackward.position.getParent();
f6cc12
                }
f6cc12
            }
04c11c
            foreach(Link link in links)
04c11c
                log.linkCreated(link);
f6cc12
            
f6cc12
            calculate(true);
04c11c
04c11c
            log.line("---- setContours end ----");
d33d2a
        }
098941
        
eb0c54
        // returns list of root contour groups
eb0c54
        // first contour in group is outer contour, others - inner
eb0c54
        public List<list<list<point>>> getContours() {</list<list<point>
eb0c54
            List<list<list<point>>> groups = new List<list<list<point>>>();</list<list<point></list<list<point>
eb0c54
            foreach(Contour root in rootContours) {
eb0c54
                List<list<point>> list = new List<list<point>>();</list<point></list<point>
eb0c54
                list.Add(contourToList(root));
eb0c54
                foreach(Contour c in root.childs)
eb0c54
                    list.Add(contourToList(c));
bb5d1a
                groups.Add(list);
098941
            }
eb0c54
            return groups;
098941
        }
eb0c54
eb0c54
        List<point> contourToList(Contour contour) {</point>
eb0c54
            List<point> list = new List<point>();</point></point>
bb5d1a
            for(Link link = contour.forward.getFirst(); link != null; link = link.contour.getNextLinear())
eb0c54
                list.Add(link.position.getParent().point);
eb0c54
            return list;
eb0c54
        }
eb0c54
f6cc12
        void resetTraceInformation() {
f6cc12
            for(int i = 0; i < contours.Count; ++i) {
f6cc12
                contours[i].forward.clear();
f6cc12
                contours[i].backward.clear();
f6cc12
                contours[i].childs.Clear();
f6cc12
                contours[i].parent = null;
f6cc12
            }
f6cc12
            contours.Clear();
f6cc12
            rootContours.Clear();
f6cc12
        }
f6cc12
        
f6cc12
        void removeLink(Link link) {
04c11c
            log.linkRemoved(link);
f6cc12
            link.position.unlink();
f6cc12
            link.contour.unlink();
f6cc12
            link.nextPosition = null;
f6cc12
            links.Remove(link);
f6cc12
        }
098941
f6cc12
        void removeEmptyPositions() {
f6cc12
            for(int i = 0; i < positions.Count; ++i)
f6cc12
                if (positions[i].links.empty())
f6cc12
                    positions.RemoveAt(i--);
f6cc12
        }
f6cc12
        
f6cc12
        void findIntersections() {
098941
            bool retry = true;
098941
            while(retry) {
098941
                retry = false;
098941
d33d2a
                // merge positions
f6cc12
                // this procedure may create zero-length links
f6cc12
                for(int i = 0; i < positions.Count; ++i) {
f6cc12
                    for(int j = i+1; j < positions.Count; ++j) {
f6cc12
                        if (positions[i].point == positions[j].point) {
f6cc12
                            while(positions[j].links.getFirst() != null)
f6cc12
                                positions[j].links.getFirst().position.insertBack(positions[i].links);
f6cc12
                            positions.RemoveAt(j--);
d33d2a
                        }
d33d2a
                    }
d33d2a
                }
f6cc12
                
d33d2a
                // remove zero-length links
f6cc12
                // this procedure may create empty positions
f6cc12
                for(int i = 0; i < links.Count; ++i)
f6cc12
                    if (links[i].position.getParent() == links[i].nextPosition)
f6cc12
                        removeLink(links[i--]);
f6cc12
                
f6cc12
                // so we need to...
f6cc12
                removeEmptyPositions();
f6cc12
                                
d33d2a
                // check intersections
f6cc12
                // this procedure may create new positions, new links and ne intersections
f6cc12
                // so we need to repeat all cycle when intersection found
f6cc12
                for(int i = 0; i < links.Count; ++i) {
f6cc12
                    for(int j = i+1; j < links.Count; ++j) {
f6cc12
                        Link a = links[i];
f6cc12
                        Link b = links[j];
f6cc12
                        Position a0 = a.position.getParent();
f6cc12
                        Position a1 = a.nextPosition;
f6cc12
                        Position b0 = b.position.getParent();
f6cc12
                        Position b1 = b.nextPosition;
f6cc12
                        Point c = new Point(0, 0);
f6cc12
                        retry = true; // will reset to false if no intersection
f6cc12
                        
f6cc12
                        switch(Geometry.findIntersection(a0.point, a1.point, b0.point, b1.point, out c))
d33d2a
                        {
f6cc12
                        case Geometry.IntersectionType.Cross:
f6cc12
                            Position p = new Position(c);
f6cc12
                            positions.Add(p);
f6cc12
                            a.split(this, p);
f6cc12
                            b.split(this, p);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Touch_a0:
f6cc12
                            b.split(this, a0);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Touch_a1:
f6cc12
                            b.split(this, a1);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Touch_b0:
f6cc12
                            a.split(this, b0);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Touch_b1:
f6cc12
                            a.split(this, b1);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_a0_b0_a1_b1:
f6cc12
                            a.split(this, b0);
f6cc12
                            b.split(this, a1);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_a0_b0_b1_a1:
f6cc12
                            a.split(this, b0).split(this, b1);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_a0_b1_a1_b0:
f6cc12
                            a.split(this, b1);
f6cc12
                            b.split(this, a1);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_a0_b1_b0_a1:
f6cc12
                            a.split(this, b1).split(this, b0);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_b0_a0_a1_b1:
f6cc12
                            b.split(this, a0).split(this, a1);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_b0_a0_b1_a1:
f6cc12
                            a.split(this, b1);
f6cc12
                            b.split(this, a0);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_b1_a0_a1_b0:
f6cc12
                            b.split(this, a1).split(this, a0);
d33d2a
                            break;
f6cc12
                        case Geometry.IntersectionType.Along_b1_a0_b0_a1:
f6cc12
                            a.split(this, b0);
f6cc12
                            b.split(this, a0);
d33d2a
                            break;
d33d2a
                        default:
d33d2a
                            retry = false;
d33d2a
                            break;
d33d2a
                        }
d33d2a
                    }
d33d2a
                }
d33d2a
            }
d33d2a
        }
d33d2a
        
f6cc12
        // This function removes link from it position
f6cc12
        // and also recursive removes part of contour to nearest fork
e5b120
        void removeLinkFromPosition(Link link) {
e5b120
            Position position = link.position.getParent();
e5b120
            Link nextToRemove = null;
e5b120
            
04c11c
            // remove back link
f6cc12
            if (link.nextPosition != null) {
f6cc12
                Link backLink = link.nextPosition.links.getFirst();
e5b120
                while (backLink != null) {
04c11c
                    if ( backLink.forward != link.forward
04c11c
                      && backLink.nextPosition == position )
e5b120
                    {
04c11c
                        removeLink(backLink);
e5b120
                        break;
e5b120
                    }
04c11c
                    backLink = backLink.position.getNextLinear();
e5b120
                }
e5b120
                
f6cc12
                if (link.nextPosition.links.getCount() == 1)
f6cc12
                    nextToRemove = link.nextPosition.links.getFirst();
e5b120
            }
e5b120
e5b120
            // remove
f6cc12
            removeLink(link);
e5b120
e5b120
            // remove next
e5b120
            if (nextToRemove != null)
e5b120
                removeLinkFromPosition(nextToRemove);
e5b120
        }
e5b120
        
f6cc12
        // Before trace contours we can (and should) to do some actions
f6cc12
        // to optimize links in each vertes.
f6cc12
        // So we can remove duplicated contour chunks
f6cc12
        // and chunks which placed inside contours of similar type
f6cc12
        // and not affecting to final image
f6cc12
        void optimizePositions() {
f6cc12
            foreach(Position position in positions) {
f6cc12
                // remove duplicates
f6cc12
                for(Link a = position.links.getFirst(); a != null; a = a.position.getNextLinear()) {
f6cc12
                    Position otherPosition = a.nextPosition;
f6cc12
    
f6cc12
                    // count forward and backward links
f6cc12
                    int count = 0;
f6cc12
                    Link forwardLink = null;
f6cc12
                    Link backwardLink = null;
f6cc12
                    Link b = a;
f6cc12
                    do {
f6cc12
                        if (b.nextPosition == otherPosition) {
f6cc12
                            if (b.forward) { forwardLink = b; ++count; }
f6cc12
                                      else { backwardLink = b; --count; }
f6cc12
                        }
f6cc12
                        b.position.getNext();
f6cc12
                    } while(b != a);
f6cc12
    
f6cc12
                    // remove extra links
f6cc12
                    Link linkToSave = count > 0 ? forwardLink
f6cc12
                                    : count < 0 ? backwardLink
f6cc12
                                    : null;
f6cc12
                    b = position.links.getFirst();
f6cc12
                    while(b != null) {
f6cc12
                        if (b.nextPosition == otherPosition && b != linkToSave) {
f6cc12
                            // remove link, a and b becomes invalid
f6cc12
                            removeLinkFromPosition(b);
f6cc12
                            // so reset a and b to repeat the current and parent cycles
f6cc12
                            b = a = position.links.getFirst();
f6cc12
                        } else {
f6cc12
                            b = b.position.getNextLinear();
f6cc12
                        }
e5b120
                    }
e5b120
                }
f6cc12
f6cc12
                if (position.links.getCount() >= 3) {
f6cc12
                    // sort links
f6cc12
                    Link first = position.links.getFirst();
f6cc12
                    Link a = first;
f6cc12
                    while (true) {
f6cc12
                        Link b = a.position.getNext();
f6cc12
                        Link c = b.position.getNext();
f6cc12
                        if ( !Geometry.isCCW(
f6cc12
                                position.point,
f6cc12
                                a.nextPosition.point,
f6cc12
                                b.nextPosition.point,
f6cc12
                                c.nextPosition.point ))
f6cc12
                        {
04c11c
                            log.linkSwapped(b, c);
f6cc12
                            b.position.swapWith(c.position);
f6cc12
                            first = a = c;
f6cc12
                            continue;
f6cc12
                        }
f6cc12
                        a = b;
f6cc12
                        if (a == first) break;
f6cc12
                    };
f6cc12
                    
f6cc12
                    // remove invisible contours from position
04c11c
                    a = first = position.links.getFirst();
04c11c
                    while(true) {
f6cc12
                        bool previous = a.position.getPrevious().forward;
f6cc12
                        bool current = a.forward;
f6cc12
                        bool next = a.position.getNext().forward;
f6cc12
                        if ( ( previous &&  current && !next)
f6cc12
                          || (!previous && !current &&  next) )
f6cc12
                        {
f6cc12
                            // remove link
f6cc12
                            removeLinkFromPosition(a);
04c11c
                            if (position.links.getCount() < 3) break;
04c11c
                            a = first = position.links.getFirst();
04c11c
                        } else {
04c11c
                            a = a.position.getNext();
04c11c
                            if (a == first) break;
f6cc12
                        }
f6cc12
                    };
e5b120
                }
e5b120
            }
e5b120
        }
e5b120
        
f6cc12
        void buildContours() {
f6cc12
            for(int i = 0; i < links.Count; ++i) {
f6cc12
                if (links[i].forward && links[i].contour.getParent() == null) {
e5b120
                    Contour contour = new Contour();
f6cc12
                    contours.Add(contour);
098941
                    
f6cc12
                    Link forwardLink = links[i];
e5b120
                    Link backwardLink = null;
098941
098941
                    do {
bb5d1a
                        if (forwardLink == null || !forwardLink.forward || forwardLink.contour.getParent() != null)
bb5d1a
                            throw new Exception();
bb5d1a
                    
098941
                        // find pair
04c11c
                        for(Link l = forwardLink.nextPosition.links.getFirst(); l != null; l = l.position.getNextLinear())
f6cc12
                            if (l.nextPosition == forwardLink.position.getParent())
098941
                                { backwardLink = l; break; }
bb5d1a
                        if (backwardLink == null || backwardLink.forward || backwardLink.contour.getParent() != null)
098941
                            throw new Exception();
098941
                        
098941
                        forwardLink.contour.insertBack(contour.forward);
367b01
                        backwardLink.contour.insertBack(contour.backward);
098941
                        
f6cc12
                        // select first link by the left (links should be CCW-ordered)
098941
                        forwardLink = backwardLink.position.getNext();
f6cc12
                    } while (forwardLink != links[i]);
098941
                }
098941
            }
098941
        }
098941
f6cc12
        // Normally we should not have free links after tracing
098941
        void removeFreeLinks() {
f6cc12
            for(int i = 0; i < links.Count; ++i)
f6cc12
                if (links[i].contour.getParent() == null)
367b01
                    throw new Exception();
098941
        }
75fa8e
f6cc12
        int countIntersectionsWithContour(Point p0, Point p1, Contour contour) {
098941
            int count = 0;
098941
            for(Link link = contour.forward.getFirst(); link != null; link = link.contour.getNextLinear()) {
f6cc12
                Point pp0 = link.position.getParent().point;
f6cc12
                Point pp1 = link.contour.getNext().position.getParent().point;
f6cc12
                int d = Math.Sign( (long)(p0.Y - p1.Y)*(long)(pp1.X - pp0.X)
f6cc12
                                 + (long)(p1.X - p0.X)*(long)(pp1.Y - pp0.Y) );
f6cc12
                Point c;
f6cc12
                switch(Geometry.findIntersection(p0, p1, pp0, pp1, out c)) {
f6cc12
                case Geometry.IntersectionType.Cross:
098941
                    count += 2*d;
098941
                    break;
f6cc12
                case Geometry.IntersectionType.Touch_b0:
098941
                    count += d;
098941
                    break;
f6cc12
                case Geometry.IntersectionType.Touch_b1:
098941
                    count -= d;
098941
                    break;
098941
                default:
098941
                    break;
e5b120
                }
e5b120
            }
098941
            return count;
098941
        }
098941
098941
        bool isContourInverted(Contour contour) {
75fa8e
            Link first = contour.forward.getFirst();
f6cc12
            Point p0 = first.position.getParent().point;
f6cc12
            Point p1 = first.contour.getNext().position.getParent().point;
f6cc12
            Geometry.makeLongestLine(p0, ref p1);
f6cc12
            return countIntersectionsWithContour(p0, p1, contour) < 0;
098941
        }
098941
        
098941
        bool isContourInside(Contour inner, Contour outer) {
75fa8e
            Link first = inner.forward.getFirst();
f6cc12
            Point p0 = first.position.getParent().point;
f6cc12
            Point p1 = first.contour.getNext().position.getParent().point;
f6cc12
            Geometry.makeLongestLine(p0, ref p1);
f6cc12
            return countIntersectionsWithContour(p0, p1, outer) != 0;
098941
        }
098941
        
f6cc12
        void organizeChildContours(Contour contour, Contour parent) {
098941
            // set parent
098941
            contour.parent = parent;
098941
098941
            // remove sub-childs from parent list
098941
            if (parent != null)
098941
                foreach(Contour c in contour.childs)
098941
                    parent.childs.Remove(c);
098941
098941
            // sub-calls
098941
            foreach(Contour c in contour.childs)
f6cc12
                organizeChildContours(c, contour);
098941
        }
098941
        
d6605c
        void invertContour(Contour contour) {
d6605c
            contour.inverted = !contour.inverted;
d6605c
            contour.forward.swapWith(contour.backward);
d6605c
            for(Link link = contour.forward.getFirst(); link != null; link.contour.getNextLinear())
d6605c
                link.forward = !link.forward;
d6605c
            for(Link link = contour.backward.getFirst(); link != null; link.contour.getNextLinear())
d6605c
                link.forward = !link.forward;
d6605c
        }
d6605c
        
f6cc12
        void buildContoursHierarhy(bool simple) {
098941
            // calculate directions of contours
f6cc12
            foreach(Contour contour in contours)
f6cc12
                contour.inverted = isContourInverted(contour);
098941
                
098941
            // find childs
f6cc12
            rootContours.AddRange(contours);
f6cc12
            for(int i = 0; i < contours.Count; ++i) {
f6cc12
                for(int j = 0; j < contours.Count; ++j) {
f6cc12
                    if (isContourInside(contours[i], contours[j])) {
f6cc12
                        contours[j].childs.Add(contours[i]);
f6cc12
                        rootContours.Remove(contours[i]);
098941
                    } else
f6cc12
                    if (isContourInside(contours[j], contours[i])) {
f6cc12
                        contours[i].childs.Add(contours[j]);
f6cc12
                        rootContours.Remove(contours[j]);
098941
                    }
367b01
                }
098941
            }
098941
            
f6cc12
            // organize childs
098941
            foreach(Contour c in rootContours)
f6cc12
                organizeChildContours(c, null);
f6cc12
           
367b01
            // remove invisible contours
f6cc12
            for(int i = 0; i < contours.Count; ++i) {
f6cc12
                bool parentInverted = contours[i].parent == null || contours[i].parent.inverted;
f6cc12
                if (parentInverted == contours[i].inverted) {
d6605c
                    if (simple) {
d6605c
                        // if simple then invert invisible contours instead of removing
d6605c
                        invertContour(contours[i]);
d6605c
                    } else {
d6605c
                        // remove contour
d6605c
                        foreach(Contour c in contours[i].childs)
d6605c
                            c.parent = contours[i].parent;
d6605c
                        List<contour> parentList = contours[i].parent == null</contour>
d6605c
                                                 ? rootContours
d6605c
                                                 : contours[i].parent.childs;
d6605c
                        parentList.AddRange(contours[i].childs);
d6605c
                        
d6605c
                        contours[i].parent = null;
d6605c
                        contours[i].childs.Clear();
d6605c
                        while(!contours[i].forward.empty())
d6605c
                            removeLink(contours[i].forward.getFirst());
d6605c
                        while(!contours[i].backward.empty())
d6605c
                            removeLink(contours[i].backward.getFirst());
d6605c
                        
d6605c
                        contours.RemoveAt(i--);
d6605c
                    }
367b01
                }
367b01
            }
e5b120
367b01
            // move contours in the holes to root
367b01
            for(int i = 0; i < rootContours.Count; ++i) {
367b01
                Contour contourA = rootContours[i];
367b01
                foreach(Contour contourB in contourA.childs) {
367b01
                    if (contourB.childs.Count != 0) {
367b01
                        foreach(Contour c in contourB.childs)
367b01
                            c.parent = null;
367b01
                        rootContours.AddRange(contourB.childs);
367b01
                    }
367b01
                }
367b01
            }
e5b120
        }
367b01
f6cc12
        void calculate(bool simple) {
f6cc12
            resetTraceInformation();
e5b120
            findIntersections();
e5b120
            optimizePositions();
f6cc12
            buildContours();
f6cc12
            removeFreeLinks();
f6cc12
            buildContoursHierarhy(simple);
098941
            removeEmptyPositions();
d33d2a
        }
eb0c54
        
eb0c54
        public void invert() {
eb0c54
            foreach(Contour contour in contours)
d6605c
                invertContour(contour);
eb0c54
        }
eb0c54
        
eb0c54
        static Shape mix(Shape a, bool invertA, Shape b, bool invertB) {
eb0c54
            Shape sum = new Shape();
04c11c
            sum.log.enabled = a.log.enabled && b.log.enabled;
04c11c
04c11c
            sum.log.line("---- mix begin ----");
eb0c54
eb0c54
            // clone a
eb0c54
            for(int i = 0; i < a.positions.Count; ++i)
eb0c54
                sum.positions.Add(new Position(a.positions[i].point));
eb0c54
            for(int i = 0; i < a.positions.Count; ++i) {
bb5d1a
                for(Link link = a.positions[i].links.getFirst(); link != null; link = link.position.getNextLinear()) {
eb0c54
                    Link l = new Link();
eb0c54
                    l.forward = invertA != link.forward;
eb0c54
                    l.nextPosition = sum.positions[a.positions.IndexOf(link.nextPosition)];
bb5d1a
                    l.position.insertBack(sum.positions[i].links);
eb0c54
                    sum.links.Add(l);
eb0c54
                }
eb0c54
            }
eb0c54
             
eb0c54
            // clone b
eb0c54
            for(int i = 0; i < b.positions.Count; ++i)
eb0c54
                sum.positions.Add(new Position(b.positions[i].point));
eb0c54
            for(int i = 0; i < b.positions.Count; ++i) {
bb5d1a
                for(Link link = b.positions[i].links.getFirst(); link != null; link = link.position.getNextLinear()) {
eb0c54
                    Link l = new Link();
eb0c54
                    l.forward = invertB != link.forward;
eb0c54
                    l.nextPosition = sum.positions[a.positions.Count + b.positions.IndexOf(link.nextPosition)];
bb5d1a
                    l.position.insertBack(sum.positions[a.positions.Count + i].links);
bb5d1a
                    sum.links.Add(l);
eb0c54
                }
eb0c54
            }
04c11c
            foreach(Link link in sum.links)
04c11c
                sum.log.linkCreated(link);
eb0c54
            
eb0c54
            sum.calculate(false);
04c11c
04c11c
            sum.log.line("---- mix end ----");
04c11c
eb0c54
            return sum;
eb0c54
        }
eb0c54
        
eb0c54
        public static Shape add(Shape a, Shape b) {
eb0c54
            return mix(a, false, b, false);
eb0c54
        }
eb0c54
eb0c54
        public static Shape subtract(Shape a, Shape b) {
eb0c54
            return mix(a, false, b, true);
eb0c54
        }
eb0c54
eb0c54
        public static Shape xor(Shape a, Shape b) {
eb0c54
            return add(subtract(a, b), subtract(b, a));
eb0c54
        }
eb0c54
eb0c54
        public static Shape intersection(Shape a, Shape b) {
eb0c54
            return subtract(add(a, b), xor(a, b));
eb0c54
        }
d33d2a
    }
d33d2a
}
d33d2a