diff --git a/mono/Contours/Circuit.cs b/mono/Contours/Circuit.cs new file mode 100644 index 0000000..5e885d0 --- /dev/null +++ b/mono/Contours/Circuit.cs @@ -0,0 +1,190 @@ +using System; + +namespace Contours { + public class Circuit where Parent: class where Child: class { + public class Entry { + readonly Child owner; + Circuit circuit; + Entry previous; + Entry next; + + public Entry(Child owner) { this.owner = owner; } + + public Child getOwner() { return owner; } + public Circuit getCircuit() { return circuit; } + public Parent getParent() { + Circuit circuit = getCircuit(); + return circuit == null ? null : circuit.getOwner(); + } + + public Entry getPreviousEntry() { return previous; } + public Entry getNextEntry() { return next; } + + public Entry getPreviousEntryLinear() { + Entry e = getPreviousEntry(); + return e == null || e == circuit.last ? null : e; + } + public Entry getNextEntryLinear() { + Entry e = getNextEntry(); + return e == null || e == circuit.first ? null : e; + } + + public Child getPrevious() { + Entry e = getPreviousEntry(); + return e == null ? null : e.getOwner(); + } + public Child getNext() { + Entry e = getNextEntry(); + return e == null ? null : e.getOwner(); + } + + public Child getPreviousLinear() { + Entry e = getPreviousEntryLinear(); + return e == null ? null : e.getOwner(); + } + public Child getNextLinear() { + Entry e = getNextEntryLinear(); + return e == null ? null : e.getOwner(); + } + + public void unlink() { + if (previous != null) previous.next = next; + if (next != null) next.previous = previous; + + if (circuit != null) { + if (circuit.first == this) { + circuit.first = next != this ? next : null; + circuit.last = previous != this ? previous : null; + } + --circuit.count; + circuit = null; + } + previous = null; + next = null; + } + + public void insertBack(Circuit circuit) { + unlink(); + if (circuit != null) { + if (circuit.empty()) { + this.circuit = circuit; + previous = next = this; + circuit.first = circuit.last = this; + ++circuit.count; + } else { + insertAfterOf(circuit.getLastEntry()); + } + } + } + + public void insertFront(Circuit circuit) { + unlink(); + if (circuit != null) { + if (circuit.empty()) { + this.circuit = circuit; + previous = next = this; + circuit.first = circuit.last = this; + ++circuit.count; + } else { + insertBeforeOf(circuit.getFirstEntry()); + } + } + } + + public void insertAfterOf(Entry entry) { + if (entry == this) return; + unlink(); + if (entry == null || entry.getCircuit() == null) return; + + previous = entry; + next = entry.next; + previous.next = this; + if (next != null) next.previous = this; + circuit = entry.getCircuit(); + + if (circuit != null) { + if (circuit.getLastEntry() == entry) + circuit.last = this; + ++circuit.count; + } + } + + public void insertBeforeOf(Entry entry) { + if (entry == this) return; + unlink(); + if (entry == null || entry.getCircuit() == null) return; + + previous = entry.previous; + next = entry; + if (previous != null) previous.next = this; + next.previous = this; + circuit = entry.getCircuit(); + + if (circuit != null) { + if (circuit.getFirstEntry() == entry) + circuit.first = this; + ++circuit.count; + } + } + + public void swapWith(Entry other) { + if (other == this || other == null) return; + + Circuit otherCircuit = other.circuit; + Entry otherPrevious = other.previous; + Entry otherNext = other.next; + + other.circuit = circuit; + other.previous = previous; + other.next = next; + + if (otherCircuit != null) { + if (otherCircuit.first == other) + otherCircuit.first = this; + if (otherCircuit.last == other) + otherCircuit.last = this; + } + + if (circuit != null) { + if (circuit.first == this) + circuit.first = other; + if (circuit.last == this) + circuit.last = other; + } + + circuit = otherCircuit; + previous = otherPrevious; + next = otherNext; + } + } + + readonly Parent owner; + Entry first; + Entry last; + int count = 0; + + public Circuit(Parent owner) { this.owner = owner; } + + public Parent getOwner() { return owner; } + public int getCount() { return count; } + + public Entry getFirstEntry() { return first; } + public Entry getLastEntry() { return last; } + + public Child getFirst() { + Entry e = getFirstEntry(); + return e == null ? null : e.getOwner(); + } + public Child getLast() { + Entry e = getLastEntry(); + return e == null ? null : e.getOwner(); + } + + public bool empty() { return getFirstEntry() == null; } + + public void clear() { + while(!empty()) getFirstEntry().unlink(); + } + } +} + diff --git a/mono/Contours/ContourFloat.cs b/mono/Contours/ContourFloat.cs deleted file mode 100644 index 890bf11..0000000 --- a/mono/Contours/ContourFloat.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Contours { - public struct VectorFloat { - public float x, y; - - public VectorFloat(float x, float y) { - this.x = x; - this.y = y; - } - } - - public class ContourFloat { - public readonly List> contours = new List>(); - - public ContourInt toContourInt(float detalization = 10000f) { - ContourInt contourInt = new ContourInt(); - - bool found = false; - VectorFloat min = new VectorFloat(0f, 0f); - VectorFloat max = new VectorFloat(0f, 0f); - foreach(List contour in contours) { - foreach(VectorFloat point in contour) { - if (!found) { - min = max = point; - found = true; - } else { - if (min.x > point.x) - min.x = point.x; - if (min.y > point.y) - min.y = point.y; - if (max.x > point.x) - max.x = point.x; - if (max.y > point.y) - max.y = point.y; - } - } - } - - if (found) { - contourInt.scale = Math.Max(max.x - min.x, max.y - min.y)/detalization; - foreach(List contour in contours) { - List newContour = new List(); - foreach(VectorFloat point in contour) - newContour.Add(new VectorInt((int)Math.Round(point.x*contourInt.scale), (int)Math.Round(point.y*contourInt.scale))); - contourInt.contours.Add(newContour); - } - } - - return contourInt; - } - } -} - diff --git a/mono/Contours/ContourInt.cs b/mono/Contours/ContourInt.cs deleted file mode 100644 index 1005f4b..0000000 --- a/mono/Contours/ContourInt.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Contours { - public struct VectorInt { - public int x, y; - - public VectorInt(int x, int y) { - this.x = x; - this.y = y; - } - } - - public class ContourInt { - public static readonly int MaxValue = 1 << 24; - - public float scale = 1f; - public readonly List> contours = new List>(); - - public ContourFloat toContourFloat() { - ContourFloat contourFloat = new ContourFloat(); - foreach(List contour in contours) { - List newContour = new List(); - foreach(VectorInt point in contour) - newContour.Add(new VectorFloat(point.x * scale, point.y * scale)); - contourFloat.contours.Add(newContour); - } - return contourFloat; - } - } -} - diff --git a/mono/Contours/Contours.csproj b/mono/Contours/Contours.csproj index db132e3..5c4af5f 100644 --- a/mono/Contours/Contours.csproj +++ b/mono/Contours/Contours.csproj @@ -34,9 +34,9 @@ - - + + diff --git a/mono/Contours/Geometry.cs b/mono/Contours/Geometry.cs new file mode 100644 index 0000000..a9a1ce1 --- /dev/null +++ b/mono/Contours/Geometry.cs @@ -0,0 +1,175 @@ +using System; +using System.Drawing; + +namespace Contours { + public static class Geometry { + public enum IntersectionType { + None, + Cross, + Identical, + Inverted, + Touch_a0, + Touch_a1, + Touch_b0, + Touch_b1, + Touch_a0_b0, + Touch_a0_b1, + Touch_a1_b0, + Touch_a1_b1, + Along_a0_b0_a1_b1, + Along_a0_b0_b1_a1, + Along_a0_b1_a1_b0, + Along_a0_b1_b0_a1, + Along_b0_a0_a1_b1, + Along_b0_a0_b1_a1, + Along_b1_a0_a1_b0, + Along_b1_a0_b0_a1 + } + + // Compare to points place at line base0-base1 + public static int comparePointsAtLine(Point a0, Point a1, Point base0, Point base1) { + if (base0.X < base1.X && a0.X < a1.X) return -1; + if (base0.X < base1.X && a1.X < a0.X) return 1; + if (base1.X < base0.X && a0.X < a1.X) return 1; + if (base1.X < base0.X && a1.X < a0.X) return -1; + if (base0.Y < base1.Y && a0.Y < a1.Y) return -1; + if (base0.Y < base1.Y && a1.Y < a0.Y) return 1; + if (base1.Y < base0.Y && a0.Y < a1.Y) return 1; + if (base1.Y < base0.Y && a1.Y < a0.Y) return -1; + return 0; + } + + public static IntersectionType findIntersection(Point a0, Point a1, Point b0, Point b1, out Point c) { + c = new Point(0, 0); + Point da = new Point(a1.X - a0.X, a1.Y - a0.Y); + Point db = new Point(b1.X - b0.X, b1.Y - b0.Y); + + if (a0.X == b0.X && a0.Y == b0.Y && a1.X == b1.X && a1.Y == b1.Y) + return IntersectionType.Identical; + + if (a0.X == b1.X && a0.Y == b1.Y && a1.X == b0.X && a1.Y == b0.Y) + return IntersectionType.Inverted; + + long divider = (long)da.X*(long)db.Y - (long)db.X*(long)da.Y; + if (divider == 0) { + if ((long)da.X*(long)(b0.Y - a0.Y) != (long)da.Y*(long)(b0.X - a0.X)) + return IntersectionType.None; + + int a0b0 = comparePointsAtLine(a0, b0, a0, a1); + int a0b1 = comparePointsAtLine(a0, b1, a0, a1); + int a1b0 = comparePointsAtLine(a1, b0, a0, a1); + int a1b1 = comparePointsAtLine(a1, b1, a0, a1); + int b0b1 = comparePointsAtLine(b0, b1, a0, a1); + int b0a0 = -a0b0; + int b0a1 = -a1b0; + int b1a0 = -a0b1; + int b1a1 = -a1b1; + int b1b0 = -b0b1; + + // a0a1b0b1 + if (a1b0 == 0 && b0b1 < 0) + return IntersectionType.Touch_a1_b0; + // a0a1b1b0 + if (a1b1 == 0 && b1b0 < 0) + return IntersectionType.Touch_a1_b1; + // b0b1a0a1 + if (b0b1 < 0 && b1a0 == 0) + return IntersectionType.Touch_a0_b1; + // b1b0a0a1 + if (b1b0 < 0 && b0a0 == 0) + return IntersectionType.Touch_a0_b0; + + if (a0b0 <= 0 && b0a1 <= 0 && a1b1 <= 0) + return IntersectionType.Along_a0_b0_a1_b1; + if (a0b0 <= 0 && b0b1 <= 0 && b1a1 <= 0) + return IntersectionType.Along_a0_b0_b1_a1; + if (a0b1 <= 0 && b1a1 <= 0 && a1b0 <= 0) + return IntersectionType.Along_a0_b1_a1_b0; + if (a0b1 <= 0 && b1b0 <= 0 && b0a1 <= 0) + return IntersectionType.Along_a0_b1_b0_a1; + if (b0a0 <= 0 && /* a0a1 */ a1b1 <= 0) + return IntersectionType.Along_b0_a0_a1_b1; + if (b0a0 <= 0 && a0b1 <= 0 && b1a1 <= 0) + return IntersectionType.Along_b0_a0_b1_a1; + if (b1a0 <= 0 && /* a0a1 */ a1b0 <= 0) + return IntersectionType.Along_b1_a0_a1_b0; + if (b1a0 <= 0 && a0b0 <= 0 && b0a1 <= 0) + return IntersectionType.Along_b1_a0_b0_a1; + + return IntersectionType.None; + } + + if (a0.X == b0.X && a0.Y == b0.Y) + return IntersectionType.Touch_a0_b0; + if (a0.X == b1.X && a0.Y == b1.Y) + return IntersectionType.Touch_a0_b1; + if (a1.X == b0.X && a1.Y == b0.Y) + return IntersectionType.Touch_a1_b0; + if (a1.X == b1.X && a1.Y == b1.Y) + return IntersectionType.Touch_a1_b1; + + long numeratorX = (long)da.X*((long)b1.Y*(long)b0.X - (long)b0.Y*(long)b1.X) + - (long)db.X*((long)a1.Y*(long)a0.X - (long)a0.Y*(long)a1.X); + long numeratorY = (long)db.Y*((long)a1.X*(long)a0.Y - (long)a0.X*(long)a1.Y) + - (long)da.Y*((long)b1.X*(long)b0.Y - (long)b0.X*(long)b1.Y); + Point p = new Point((int)(numeratorX/divider), (int)(numeratorY/divider)); + if (comparePointsAtLine(p, a0, a0, a1) < 0 || comparePointsAtLine(p, a1, a0, a1) > 0) + return IntersectionType.None; + + if (p.X == a0.X && p.Y == a0.Y) + return IntersectionType.Touch_a0; + if (p.X == a1.X && p.Y == a1.Y) + return IntersectionType.Touch_a1; + if (p.X == b0.X && p.Y == b0.Y) + return IntersectionType.Touch_b0; + if (p.X == b1.X && p.Y == b1.Y) + return IntersectionType.Touch_b1; + + c = p; + return IntersectionType.Cross; + } + + public static bool isCCW(Point a, Point b, Point c) { + long d = (long)a.X*(long)c.Y - (long)a.Y*(long)c.X; + // angle AC < 180 deg + if (d > 0) + return (long)a.X*(long)b.Y >= (long)a.Y*(long)b.X + && (long)c.X*(long)b.Y <= (long)c.Y*(long)b.X; + // angle AC > 180 deg + if (d < 0) + return (long)a.X*(long)b.Y >= (long)a.Y*(long)b.X + || (long)c.X*(long)b.Y <= (long)c.Y*(long)b.X; + // angle AC == 180 deg + if ((a.X >= 0) != (c.X >= 0) || (a.Y >= 0) != (c.Y >= 0)) + return (long)a.X*(long)b.Y >= (long)a.Y*(long)b.X; + // angle AC == 0 deg + return true; + } + + public static bool isCCW(Point center, Point a, Point b, Point c) { + return isCCW( + new Point(a.X - center.X, a.Y - center.Y), + new Point(b.X - center.X, b.Y - center.Y), + new Point(c.X - center.X, c.Y - center.Y) ); + } + + public static void makeLongestLine(Point p0, ref Point p1) { + int MaxValue = int.MaxValue << 1; + Point direction = new Point(p1.X - p0.X, p1.Y - p0.Y); + int amplifierX = + direction.X > 0 ? (MaxValue - p0.X)/direction.X + 1 + : direction.X < 0 ? (MaxValue + p0.X)/(-direction.X) + 1 + : int.MaxValue; + int amplifierY = + direction.Y > 0 ? (MaxValue - p0.Y)/direction.Y + 1 + : direction.Y < 0 ? (MaxValue + p0.Y)/(-direction.Y) + 1 + : int.MaxValue; + int amplifier = Math.Min(amplifierX, amplifierY); + p1 = new Point( + p0.X + direction.X*amplifier, + p0.Y + direction.Y*amplifier ); + } + + } +} + diff --git a/mono/Contours/MainForm.cs b/mono/Contours/MainForm.cs index f68a6c2..bdfabcc 100644 --- a/mono/Contours/MainForm.cs +++ b/mono/Contours/MainForm.cs @@ -14,11 +14,11 @@ namespace Contours { } bool drawing = false; - ContourFloat contour = new ContourFloat(); + List> contours = new List>(); private void mouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { - contour.contours.Add(new List()); + contours.Add(new List()); drawing = true; mouseMove(sender, e); } @@ -26,7 +26,7 @@ namespace Contours { private void mouseMove(object sender, MouseEventArgs e) { if (drawing) { - contour.contours.Last().Add(new VectorFloat(e.Location.X, e.Location.Y)); + contours.Last().Add(new PointF(e.Location.X, e.Location.Y)); Refresh(); } } @@ -38,20 +38,17 @@ namespace Contours { } if (e.Button == MouseButtons.Right) { drawing = false; - contour.contours.Clear(); + contours.Clear(); Refresh(); } } private void paint(object sender, PaintEventArgs e) { e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; - foreach(List c in contour.contours) { + foreach(List c in contours) { if (c != null && c.Count >= 3) { - List newContour = new List(); - foreach(VectorFloat point in c) - newContour.Add(new PointF(point.x, point.y)); - newContour.Add(newContour.First()); - e.Graphics.DrawLines(Pens.Black, newContour.ToArray()); + e.Graphics.DrawLines(Pens.Black, c.ToArray()); + e.Graphics.DrawLine(Pens.Black, c.First(), c.Last()); } } } diff --git a/mono/Contours/Shape.cs b/mono/Contours/Shape.cs index 13f8de8..809b0a1 100644 --- a/mono/Contours/Shape.cs +++ b/mono/Contours/Shape.cs @@ -1,426 +1,121 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Drawing; namespace Contours { - public enum IntersectionType { - None, - Cross, - Identical, - Inverted, - Touch_a0, - Touch_a1, - Touch_b0, - Touch_b1, - Touch_a0_b0, - Touch_a0_b1, - Touch_a1_b0, - Touch_a1_b1, - Along_a0_b0_a1_b1, - Along_a0_b0_b1_a1, - Along_a0_b1_a1_b0, - Along_a0_b1_b0_a1, - Along_b0_a0_a1_b1, - Along_b0_a0_b1_a1, - Along_b1_a0_a1_b0, - Along_b1_a0_b0_a1 - } - - public class Circuit where Parent: class where Child: class { - public class Entry { - readonly Child owner; - Circuit circuit; - Entry previous; - Entry next; - - public Entry(Child owner) { this.owner = owner; } - - public Child getOwner() { return owner; } - public Circuit getCircuit() { return circuit; } - public Parent getParent() { - Circuit circuit = getCircuit(); - return circuit == null ? null : circuit.getOwner(); - } - - public Entry getPreviousEntry() { return previous; } - public Entry getNextEntry() { return next; } - - public Entry getPreviousEntryLinear() { - Entry e = getPreviousEntry(); - return e == null || e == circuit.last ? null : e; - } - public Entry getNextEntryLinear() { - Entry e = getNextEntry(); - return e == null || e == circuit.first ? null : e; - } - - public Child getPrevious() { - Entry e = getPreviousEntry(); - return e == null ? null : e.getOwner(); - } - public Child getNext() { - Entry e = getNextEntry(); - return e == null ? null : e.getOwner(); - } - - public Child getPreviousLinear() { - Entry e = getPreviousEntryLinear(); - return e == null ? null : e.getOwner(); - } - public Child getNextLinear() { - Entry e = getNextEntryLinear(); - return e == null ? null : e.getOwner(); - } - - public void unlink() { - if (previous != null) previous.next = next; - if (next != null) next.previous = previous; - - if (circuit != null) { - if (circuit.first == this) { - circuit.first = next != this ? next : null; - circuit.last = previous != this ? previous : null; - } - --circuit.count; - circuit = null; - } - previous = null; - next = null; - } - - public void insertBack(Circuit circuit) { - unlink(); - if (circuit != null) { - if (circuit.empty()) { - this.circuit = circuit; - previous = next = this; - circuit.first = circuit.last = this; - ++circuit.count; - } else { - insertAfterOf(circuit.getLastEntry()); - } - } - } - - public void insertFront(Circuit circuit) { - unlink(); - if (circuit != null) { - if (circuit.empty()) { - this.circuit = circuit; - previous = next = this; - circuit.first = circuit.last = this; - ++circuit.count; - } else { - insertBeforeOf(circuit.getFirstEntry()); - } - } - } - - public void insertAfterOf(Entry entry) { - if (entry == this) return; - unlink(); - if (entry == null || entry.getCircuit() == null) return; - - previous = entry; - next = entry.next; - previous.next = this; - if (next != null) next.previous = this; - circuit = entry.getCircuit(); - - if (circuit != null) { - if (circuit.getLastEntry() == entry) - circuit.last = this; - ++circuit.count; - } - } - - public void insertBeforeOf(Entry entry) { - if (entry == this) return; - unlink(); - if (entry == null || entry.getCircuit() == null) return; - - previous = entry.previous; - next = entry; - if (previous != null) previous.next = this; - next.previous = this; - circuit = entry.getCircuit(); - - if (circuit != null) { - if (circuit.getFirstEntry() == entry) - circuit.first = this; - ++circuit.count; - } - } - - public void swapWith(Entry other) { - if (other == this || other == null) return; - - Circuit otherCircuit = other.circuit; - Entry otherPrevious = other.previous; - Entry otherNext = other.next; - - other.circuit = circuit; - other.previous = previous; - other.next = next; - - if (otherCircuit != null) { - if (otherCircuit.first == other) - otherCircuit.first = this; - if (otherCircuit.last == other) - otherCircuit.last = this; - } - - if (circuit != null) { - if (circuit.first == this) - circuit.first = other; - if (circuit.last == this) - circuit.last = other; - } - - circuit = otherCircuit; - previous = otherPrevious; - next = otherNext; - } - } - - readonly Parent owner; - Entry first; - Entry last; - int count = 0; - - public Circuit(Parent owner) { this.owner = owner; } - - public Parent getOwner() { return owner; } - public int getCount() { return count; } - - public Entry getFirstEntry() { return first; } - public Entry getLastEntry() { return last; } - - public Child getFirst() { - Entry e = getFirstEntry(); - return e == null ? null : e.getOwner(); - } - public Child getLast() { - Entry e = getLastEntry(); - return e == null ? null : e.getOwner(); - } - - public bool empty() { return getFirstEntry() == null; } - - public void clear() { - while(!empty()) getFirstEntry().unlink(); - } - } - public class Shape { - public class Position { - public int x = 0; - public int y = 0; - public readonly Circuit.Entry shape; + class Position { + public Point point; public readonly Circuit links; public Position() { - shape = new Circuit.Entry(this); links = new Circuit(this); } - public Position(int x, int y): this() { this.x = x; this.y = y; } - - public VectorInt toVectorInt() { return new VectorInt(x, y); } + public Position(Point point): this() { this.point = point; } } - public class Contour { + class Contour { public bool inverted = false; public Contour parent; public List childs; - public readonly Circuit.Entry shape; public readonly Circuit forward; public readonly Circuit backward; public Contour() { - shape = new Circuit.Entry(this); forward = new Circuit(this); backward = new Circuit(this); } } - public class Link { - public readonly Circuit.Entry shape; + class Link { + public bool forward = false; + public Position nextPosition = null; + public readonly Circuit.Entry position; public readonly Circuit.Entry contour; - - public bool forward = false; - public Position target = null; public Link() { - shape = new Circuit.Entry(this); position = new Circuit.Entry(this); contour = new Circuit.Entry(this); } - public void unlink() { - contour.unlink(); - position.unlink(); - shape.unlink(); - target = null; - } - - public static Link create(Position position, Circuit contourCircuit) { + public Link split(Shape shape, Position position) { Link link = new Link(); - link.shape.insertBack(contourCircuit.getOwner().shape.getParent().links); - link.position.insertBack(position.links); - link.contour.insertBack(contourCircuit); - link.forward = contourCircuit == contourCircuit.getOwner().forward; - return link; - } - - public Link createSplitAfter(Position position) { - Link link = new Link(); - link.forward = forward; - link.shape.insertBack(shape.getParent().links); link.position.insertBack(position.links); link.contour.insertAfterOf(contour); + link.forward = forward; + link.nextPosition = nextPosition; + nextPosition = position; + shape.links.Add(link); return link; } - public Link createSplitAfter(Circuit.Entry position) { - Link link = new Link(); - link.forward = forward; - link.shape.insertBack(shape.getParent().links); - link.position.insertAfterOf(position); - link.contour.insertAfterOf(contour); - return link; + public Link split(Shape shape, Link positionLink) { + return split(shape, positionLink.position.getParent()); } } - public Circuit positions; - public Circuit links; - public Circuit contours; - public List rootContours; - public void addContours(ContourInt contours) { - foreach(List contour in contours.contours) - addContour(contour); - } + readonly List positions = new List(); + readonly List links = new List(); + readonly List contours = new List(); + readonly List rootContours = new List(); - public void addContour(ICollection points) { - if (points.Count < 3) - return; - - Contour contour = new Contour(); - contour.shape.insertBack(contours); - foreach(VectorInt point in points) { - Position position = new Position(point.x, point.y); - position.shape.insertBack(positions); - Link.create(position, contour.forward); - Link.create(position, contour.backward); - } + + public void clear() { + positions.Clear(); + links.Clear(); + contours.Clear(); + rootContours.Clear(); } - public static int compare(VectorInt a0, VectorInt a1, VectorInt base0, VectorInt base1) { - if (base0.x < base1.x && a0.x < a1.x) return -1; - if (base0.x < base1.x && a1.x < a0.x) return 1; - if (base1.x < base0.x && a0.x < a1.x) return 1; - if (base1.x < base0.x && a1.x < a0.x) return -1; - if (base0.y < base1.x && a0.y < a1.x) return -1; - if (base0.y < base1.x && a1.y < a0.x) return 1; - if (base1.y < base0.x && a0.y < a1.x) return 1; - if (base1.y < base0.x && a1.y < a0.x) return -1; - return 0; + public void setContour(IEnumerable contour) { + setContours(new IEnumerable[] { contour }); } - public static IntersectionType findIntersection(VectorInt a0, VectorInt a1, VectorInt b0, VectorInt b1, out VectorInt c) { - c = new VectorInt(); - VectorInt da = new VectorInt(a1.x - a0.x, a1.y - a0.y); - VectorInt db = new VectorInt(b1.x - b0.x, b1.y - b0.y); - - if (a0.x == b0.x && a0.y == b0.y && a1.x == b1.x && a1.y == b1.y) - return IntersectionType.Identical; - - if (a0.x == b1.x && a0.y == b1.y && a1.x == b0.x && a1.y == b0.y) - return IntersectionType.Inverted; - - long divider = (long)da.x*(long)db.y - (long)db.x*(long)da.y; - if (divider == 0) { - if ((long)da.x*(long)(b0.y - a0.y) != (long)da.y*(long)(b0.x - a0.x)) - return IntersectionType.None; - - int a0b0 = compare(a0, b0, a0, a1); - int a0b1 = compare(a0, b1, a0, a1); - int a1b0 = compare(a1, b0, a0, a1); - int a1b1 = compare(a1, b1, a0, a1); - int b0b1 = compare(b0, b1, a0, a1); - int b0a0 = -a0b0; - int b0a1 = -a1b0; - int b1a0 = -a0b1; - int b1a1 = -a1b1; - int b1b0 = -b0b1; - - // a0a1b0b1 - if (a1b0 == 0 && b0b1 < 0) - return IntersectionType.Touch_a1_b0; - // a0a1b1b0 - if (a1b1 == 0 && b1b0 < 0) - return IntersectionType.Touch_a1_b1; - // b0b1a0a1 - if (b0b1 < 0 && b1a0 == 0) - return IntersectionType.Touch_a0_b1; - // b1b0a0a1 - if (b1b0 < 0 && b0a0 == 0) - return IntersectionType.Touch_a0_b0; - - if (a0b0 <= 0 && b0a1 <= 0 && a1b1 <= 0) - return IntersectionType.Along_a0_b0_a1_b1; - if (a0b0 <= 0 && b0b1 <= 0 && b1a1 <= 0) - return IntersectionType.Along_a0_b0_b1_a1; - if (a0b1 <= 0 && b1a1 <= 0 && a1b0 <= 0) - return IntersectionType.Along_a0_b1_a1_b0; - if (a0b1 <= 0 && b1b0 <= 0 && b0a1 <= 0) - return IntersectionType.Along_a0_b1_b0_a1; - if (b0a0 <= 0 && /* a0a1 */ a1b1 <= 0) - return IntersectionType.Along_b0_a0_a1_b1; - if (b0a0 <= 0 && a0b1 <= 0 && b1a1 <= 0) - return IntersectionType.Along_b0_a0_b1_a1; - if (b1a0 <= 0 && /* a0a1 */ a1b0 <= 0) - return IntersectionType.Along_b1_a0_a1_b0; - if (b1a0 <= 0 && a0b0 <= 0 && b0a1 <= 0) - return IntersectionType.Along_b1_a0_b0_a1; - - return IntersectionType.None; - } + public void setContours(IEnumerable> contours) { + clear(); + foreach(IEnumerable contour in contours) { + if (contour.Count() >= 3) { + Link firstForward = null; + Link firstBackward = null; + Link previousForward = null; + Link previousBackward = null; + foreach(Point point in contour) { + Position position = new Position(point); - if (a0.x == b0.x && a0.y == b0.y) - return IntersectionType.Touch_a0_b0; - if (a0.x == b1.x && a0.y == b1.y) - return IntersectionType.Touch_a0_b1; - if (a1.x == b0.x && a1.y == b0.y) - return IntersectionType.Touch_a1_b0; - if (a1.x == b1.x && a1.y == b1.y) - return IntersectionType.Touch_a1_b1; - - long numeratorX = (long)da.x*((long)b1.y*(long)b0.x - (long)b0.y*(long)b1.x) - - (long)db.x*((long)a1.y*(long)a0.x - (long)a0.y*(long)a1.x); - long numeratorY = (long)db.y*((long)a1.x*(long)a0.y - (long)a0.x*(long)a1.y) - - (long)da.y*((long)b1.x*(long)b0.y - (long)b0.x*(long)b1.y); - VectorInt p = new VectorInt((int)(numeratorX/divider), (int)(numeratorY/divider)); - if (compare(p, a0, a0, a1) < 0 || compare(p, a1, a0, a1) > 0) - return IntersectionType.None; - - if (p.x == a0.x && p.y == a0.y) - return IntersectionType.Touch_a0; - if (p.x == a1.x && p.y == a1.y) - return IntersectionType.Touch_a1; - if (p.x == b0.x && p.y == b0.y) - return IntersectionType.Touch_b0; - if (p.x == b1.x && p.y == b1.y) - return IntersectionType.Touch_b1; - - c = p; - return IntersectionType.Cross; + Link forward = new Link(); + forward.forward = true; + forward.position.insertBack(position.links); + if (previousForward != null) + previousForward.nextPosition = forward.position.getParent(); + links.Add(forward); + + Link backward = new Link(); + backward.forward = false; + backward.position.insertBack(position.links); + if (previousBackward != null) + backward.nextPosition = previousBackward.position.getParent(); + links.Add(backward); + + if (firstForward == null) firstForward = forward; + if (firstBackward == null) firstBackward = backward; + previousForward = forward; + previousBackward = backward; + } + previousForward.nextPosition = firstForward.position.getParent(); + firstBackward.nextPosition = previousBackward.position.getParent(); + } + } + + calculate(true); } + /* TODO: public bool removeEmptyContours() { bool removed = false; @@ -444,113 +139,119 @@ namespace Contours { return removed; } + */ + + void resetTraceInformation() { + for(int i = 0; i < contours.Count; ++i) { + contours[i].forward.clear(); + contours[i].backward.clear(); + contours[i].childs.Clear(); + contours[i].parent = null; + } + contours.Clear(); + rootContours.Clear(); + } + + void removeLink(Link link) { + link.position.unlink(); + link.contour.unlink(); + link.nextPosition = null; + links.Remove(link); + } - public void findIntersections() { + void removeEmptyPositions() { + for(int i = 0; i < positions.Count; ++i) + if (positions[i].links.empty()) + positions.RemoveAt(i--); + } + + void findIntersections() { bool retry = true; while(retry) { retry = false; - retry = removeEmptyContours(); - if (retry) continue; - - // remove empty positions - for(Position position = positions.getFirst(); !retry && position != null; position = position.shape.getNextLinear()) { - if (position.links.empty()) { - position.shape.unlink(); - retry = true; - break; - } - } - if (retry) continue; - // merge positions - for(Position positionA = positions.getFirst(); !retry && positionA != null; positionA = positionA.shape.getNextLinear()) { - for(Position positionB = positionA.shape.getNextLinear(); !retry && positionB != null; positionB = positionB.shape.getNextLinear()) { - if (positionA.x == positionB.x && positionA.y == positionB.y) { - while(positionB.links.getFirst() != null) - positionB.links.getFirst().position.insertBack(positionA.links); - positionB.shape.unlink(); - retry = true; - break; + // this procedure may create zero-length links + for(int i = 0; i < positions.Count; ++i) { + for(int j = i+1; j < positions.Count; ++j) { + if (positions[i].point == positions[j].point) { + while(positions[j].links.getFirst() != null) + positions[j].links.getFirst().position.insertBack(positions[i].links); + positions.RemoveAt(j--); } } } - if (retry) continue; - + // remove zero-length links - for(Link linkA0 = links.getFirst(); !retry && linkA0 != null; linkA0 = linkA0.shape.getNextLinear()) { - Link linkA1 = linkA0.contour.getNext(); - if (linkA0.position.getParent() == linkA1.position.getParent()) { - linkA1.unlink(); - retry = true; - break; - } - } - if (retry) continue; - + // this procedure may create empty positions + for(int i = 0; i < links.Count; ++i) + if (links[i].position.getParent() == links[i].nextPosition) + removeLink(links[i--]); + + // so we need to... + removeEmptyPositions(); + // check intersections - for(Link linkA0 = links.getFirst(); !retry && linkA0 != null; linkA0 = linkA0.shape.getNextLinear()) { - Link linkA1 = linkA0.contour.getNext(); - for(Link linkB0 = links.getFirst(); !retry && linkB0 != null; linkB0 = linkB0.shape.getNextLinear()) { - Link linkB1 = linkB0.contour.getNext(); - VectorInt cross = new VectorInt(0, 0); - Position position; - retry = true; - switch( findIntersection( linkA0.position.getParent().toVectorInt(), - linkA1.position.getParent().toVectorInt(), - linkB0.position.getParent().toVectorInt(), - linkB1.position.getParent().toVectorInt(), - out cross )) + // this procedure may create new positions, new links and ne intersections + // so we need to repeat all cycle when intersection found + for(int i = 0; i < links.Count; ++i) { + for(int j = i+1; j < links.Count; ++j) { + Link a = links[i]; + Link b = links[j]; + Position a0 = a.position.getParent(); + Position a1 = a.nextPosition; + Position b0 = b.position.getParent(); + Position b1 = b.nextPosition; + Point c = new Point(0, 0); + retry = true; // will reset to false if no intersection + + switch(Geometry.findIntersection(a0.point, a1.point, b0.point, b1.point, out c)) { - case IntersectionType.Cross: - position = new Position(cross.x, cross.y); - position.shape.insertBack(positions); - linkA0.createSplitAfter(position); - linkB0.createSplitAfter(position); + case Geometry.IntersectionType.Cross: + Position p = new Position(c); + positions.Add(p); + a.split(this, p); + b.split(this, p); break; - case IntersectionType.Touch_a0: - linkB0.createSplitAfter(linkA0.position); + case Geometry.IntersectionType.Touch_a0: + b.split(this, a0); break; - case IntersectionType.Touch_a1: - linkB0.createSplitAfter(linkA1.position); + case Geometry.IntersectionType.Touch_a1: + b.split(this, a1); break; - case IntersectionType.Touch_b0: - linkA0.createSplitAfter(linkB0.position); + case Geometry.IntersectionType.Touch_b0: + a.split(this, b0); break; - case IntersectionType.Touch_b1: - linkA0.createSplitAfter(linkB1.position); + case Geometry.IntersectionType.Touch_b1: + a.split(this, b1); break; - case IntersectionType.Along_a0_b0_a1_b1: - linkA0.createSplitAfter(linkB0.position); - linkB0.createSplitAfter(linkA1.position); + case Geometry.IntersectionType.Along_a0_b0_a1_b1: + a.split(this, b0); + b.split(this, a1); break; - case IntersectionType.Along_a0_b0_b1_a1: - linkA0.createSplitAfter(linkB0.position) - .createSplitAfter(linkB1.position); + case Geometry.IntersectionType.Along_a0_b0_b1_a1: + a.split(this, b0).split(this, b1); break; - case IntersectionType.Along_a0_b1_a1_b0: - linkA0.createSplitAfter(linkB1.position); - linkB0.createSplitAfter(linkA1.position); + case Geometry.IntersectionType.Along_a0_b1_a1_b0: + a.split(this, b1); + b.split(this, a1); break; - case IntersectionType.Along_a0_b1_b0_a1: - linkA0.createSplitAfter(linkB1.position) - .createSplitAfter(linkB0.position); + case Geometry.IntersectionType.Along_a0_b1_b0_a1: + a.split(this, b1).split(this, b0); break; - case IntersectionType.Along_b0_a0_a1_b1: - linkB0.createSplitAfter(linkA0.position) - .createSplitAfter(linkA1.position); + case Geometry.IntersectionType.Along_b0_a0_a1_b1: + b.split(this, a0).split(this, a1); break; - case IntersectionType.Along_b0_a0_b1_a1: - linkA0.createSplitAfter(linkB1.position); - linkB0.createSplitAfter(linkA0.position); + case Geometry.IntersectionType.Along_b0_a0_b1_a1: + a.split(this, b1); + b.split(this, a0); break; - case IntersectionType.Along_b1_a0_a1_b0: - linkB0.createSplitAfter(linkA1.position) - .createSplitAfter(linkA0.position); + case Geometry.IntersectionType.Along_b1_a0_a1_b0: + b.split(this, a1).split(this, a0); break; - case IntersectionType.Along_b1_a0_b0_a1: - linkA0.createSplitAfter(linkB0.position); - linkB0.createSplitAfter(linkA0.position); + case Geometry.IntersectionType.Along_b1_a0_b0_a1: + a.split(this, b0); + b.split(this, a0); break; default: retry = false; @@ -558,85 +259,18 @@ namespace Contours { } } } - if (retry) continue; } } - void unlinkContoursChains() { - while(!contours.empty()) { - Contour contour = contours.getFirst(); - while(!contour.forward.empty()) { - Link link = contour.forward.getFirst(); - link.target = link.contour.getNext().position.getParent(); - link.contour.unlink(); - } - while(!contour.backward.empty()) { - Link link = contour.backward.getFirst(); - link.target = link.contour.getNext().position.getParent(); - link.contour.unlink(); - } - contour.shape.unlink(); - contour.childs.Clear(); - contour.parent = null; - } - rootContours.Clear(); - } - - static bool compareAngle(VectorInt a, VectorInt b, VectorInt c) { - int d = a.x*c.y - a.y*c.x; - // angle AC < 180 deg - if (d > 0) - return a.x*b.y >= a.y*b.x && c.x*b.y <= c.y*b.x; - // angle AC > 180 deg - if (d < 0) - return a.x*b.y >= a.y*b.x || c.x*b.y <= c.y*b.x; - // angle AC == 180 deg - if ((a.x >= 0) != (c.x >= 0) || (a.y >= 0) != (c.y >= 0)) - return a.x*b.y >= a.y*b.x; - // angle AC == 0 deg - return true; - } - - static bool compareAngle(VectorInt center, VectorInt a, VectorInt b, VectorInt c) { - return compareAngle( new VectorInt(a.x - center.x, a.y - center.y), - new VectorInt(b.x - center.x, b.y - center.y), - new VectorInt(c.x - center.x, c.y - center.y) ); - } - - void sortLinksAtPosition(Position position) { - if (position.links.getCount() < 3) return; - Link first = position.links.getFirst(); - Link linkA = first; - while (true) { - Link linkB = linkA.position.getNext(); - Link linkC = linkB.position.getNext(); - if ( !compareAngle( - position.toVectorInt(), - linkA.target.toVectorInt(), - linkB.target.toVectorInt(), - linkC.target.toVectorInt() )) - { - linkB.position.swapWith(linkC.position); - first = linkA = linkC; - continue; - } - linkA = linkB; - if (linkA == first) break; - }; - } - - void sortLinksAtPositions() { - for(Position position = positions.getFirst(); position != null; position = position.shape.getNextLinear()) - sortLinksAtPosition(position); - } - + // This function removes link from it position + // and also recursive removes part of contour to nearest fork void removeLinkFromPosition(Link link) { Position position = link.position.getParent(); Link nextToRemove = null; // remove back link - if (link.target != null) { - Link backLink = link.target.links.getFirst(); + if (link.nextPosition != null) { + Link backLink = link.nextPosition.links.getFirst(); while (backLink != null) { Link l = backLink; backLink = backLink.position.getNextLinear(); @@ -644,164 +278,159 @@ namespace Contours { && l.contour.getNext() != null && l.contour.getNext().position.getParent() == position ) { - l.unlink(); + removeLink(l); break; } } - if (link.target.links.getCount() == 1) - nextToRemove = link.target.links.getFirst(); + if (link.nextPosition.links.getCount() == 1) + nextToRemove = link.nextPosition.links.getFirst(); } // remove - link.unlink(); + removeLink(link); // remove next if (nextToRemove != null) removeLinkFromPosition(nextToRemove); } - void removeDuplicateLinksFromPosition(Position position) { - for(Link linkA = position.links.getFirst(); linkA != null; linkA = linkA.position.getNextLinear()) { - Position otherPosition = linkA.target; - - // count forward and backward links - int count = 0; - Link forwardLink = null; - Link backwardLink = null; - Link linkB = linkA; - do { - if (linkB.target == otherPosition) { - if (linkB.forward) { forwardLink = linkB; ++count; } - else { backwardLink = linkB; --count; } - } - linkB.position.getNext(); - } while(linkB != linkA); - - // remove extra links - Link linkToSave = count > 0 ? forwardLink - : count < 0 ? backwardLink - : null; - linkB = position.links.getFirst(); - while(linkB != null) { - if (linkB.target == otherPosition && linkB != linkToSave) { - removeLinkFromPosition(linkA); - // reset linkA - linkB = linkA = position.links.getFirst(); - } else { - linkB = linkB.position.getNextLinear(); + // Before trace contours we can (and should) to do some actions + // to optimize links in each vertes. + // So we can remove duplicated contour chunks + // and chunks which placed inside contours of similar type + // and not affecting to final image + void optimizePositions() { + foreach(Position position in positions) { + // remove duplicates + for(Link a = position.links.getFirst(); a != null; a = a.position.getNextLinear()) { + Position otherPosition = a.nextPosition; + + // count forward and backward links + int count = 0; + Link forwardLink = null; + Link backwardLink = null; + Link b = a; + do { + if (b.nextPosition == otherPosition) { + if (b.forward) { forwardLink = b; ++count; } + else { backwardLink = b; --count; } + } + b.position.getNext(); + } while(b != a); + + // remove extra links + Link linkToSave = count > 0 ? forwardLink + : count < 0 ? backwardLink + : null; + b = position.links.getFirst(); + while(b != null) { + if (b.nextPosition == otherPosition && b != linkToSave) { + // remove link, a and b becomes invalid + removeLinkFromPosition(b); + // so reset a and b to repeat the current and parent cycles + b = a = position.links.getFirst(); + } else { + b = b.position.getNextLinear(); + } } } - } - } - - void removeInvisibleContoursFromPosition(Position position) { - if (position.links.getCount() < 3) return; - Link first = position.links.getFirst(); - Link link = first.position.getNext(); - while(link != first) { - bool previous = link.position.getPrevious().forward; - bool current = link.forward; - bool next = link.position.getNext().forward; - if ( ( previous && current && !next) - || (!previous && !current && next) ) - { - // remove link - removeLinkFromPosition(link); + + if (position.links.getCount() >= 3) { + // sort links + Link first = position.links.getFirst(); + Link a = first; + while (true) { + Link b = a.position.getNext(); + Link c = b.position.getNext(); + if ( !Geometry.isCCW( + position.point, + a.nextPosition.point, + b.nextPosition.point, + c.nextPosition.point )) + { + b.position.swapWith(c.position); + first = a = c; + continue; + } + a = b; + if (a == first) break; + }; + + // remove invisible contours from position first = position.links.getFirst(); - link = first.position.getNext(); - } - }; - } - - void removeEmptyPositions() { - for(Position position = positions.getFirst(); position != null;) { - if (position.links.empty()) { - Position p = position; - position = position.shape.getNextLinear(); - p.shape.unlink(); - } else { - position = position.shape.getNextLinear(); + a = first.position.getNext(); + while(a != first) { + bool previous = a.position.getPrevious().forward; + bool current = a.forward; + bool next = a.position.getNext().forward; + if ( ( previous && current && !next) + || (!previous && !current && next) ) + { + // remove link + removeLinkFromPosition(a); + first = position.links.getFirst(); + a = first.position.getNext(); + } + }; } } } - void optimizePositions() { - // remove extra links - for(Position position = positions.getFirst(); position != null; position = position.shape.getNextLinear()) { - removeDuplicateLinksFromPosition(position); - removeInvisibleContoursFromPosition(position); - } - } - - void traceContours() { - for(Link linkA = links.getFirst(); linkA != null; linkA = linkA.shape.getNext()) { - if (linkA.forward && linkA.contour.getParent() == null) { + void buildContours() { + for(int i = 0; i < links.Count; ++i) { + if (links[i].forward && links[i].contour.getParent() == null) { Contour contour = new Contour(); - contour.shape.insertBack(contours); + contours.Add(contour); - Link forwardLink = linkA; + Link forwardLink = links[i]; Link backwardLink = null; do { // find pair - for(Link l = linkA.target.links.getFirst(); l != null; l = l.position.getNext()) - if (l.target == linkA.position.getParent()) + for(Link l = forwardLink.nextPosition.links.getFirst(); l != null; l = l.position.getNext()) + if (l.nextPosition == forwardLink.position.getParent()) { backwardLink = l; break; } - if (backwardLink == null) + if (backwardLink == null || backwardLink.forward) throw new Exception(); forwardLink.contour.insertBack(contour.forward); backwardLink.contour.insertBack(contour.backward); + // select first link by the left (links should be CCW-ordered) forwardLink = backwardLink.position.getNext(); if ( !forwardLink.forward || forwardLink == backwardLink || forwardLink.contour.getParent() != null ) throw new Exception(); - } while (forwardLink != linkA); + } while (forwardLink != links[i]); } } } + // Normally we should not have free links after tracing void removeFreeLinks() { - for(Link linkA = links.getFirst(); linkA != null; linkA = linkA.shape.getNext()) - if (linkA.contour.getParent() == null) + for(int i = 0; i < links.Count; ++i) + if (links[i].contour.getParent() == null) throw new Exception(); } - static void makeLongestLine(VectorInt p0, ref VectorInt p1) { - VectorInt direction = new VectorInt(p1.x - p0.x, p1.y - p0.y); - int amplifierX = - direction.x > 0 ? (ContourInt.MaxValue - p0.x)/direction.x + 1 - : direction.x < 0 ? (ContourInt.MaxValue + p0.x)/(-direction.x) + 1 - : int.MaxValue; - int amplifierY = - direction.y > 0 ? (ContourInt.MaxValue - p0.y)/direction.y + 1 - : direction.y < 0 ? (ContourInt.MaxValue + p0.y)/(-direction.y) + 1 - : int.MaxValue; - int amplifier = Math.Min(amplifierX, amplifierY); - p1 = new VectorInt( - p0.x + direction.x*amplifier, - p0.y + direction.y*amplifier ); - } - - int countContourIntersections(Contour contour, VectorInt p0, VectorInt p1) { + int countIntersectionsWithContour(Point p0, Point p1, Contour contour) { int count = 0; for(Link link = contour.forward.getFirst(); link != null; link = link.contour.getNextLinear()) { - VectorInt pp0 = link.position.getParent().toVectorInt(); - VectorInt pp1 = link.contour.getNext().position.getParent().toVectorInt(); - int d = Math.Sign( (long)(p0.y - p1.y)*(long)(pp1.x - pp0.x) - + (long)(p1.x - p0.x)*(long)(pp1.y - pp0.y) ); - VectorInt c; - switch(findIntersection(p0, p1, pp0, pp1, out c)) { - case IntersectionType.Cross: + Point pp0 = link.position.getParent().point; + Point pp1 = link.contour.getNext().position.getParent().point; + int d = Math.Sign( (long)(p0.Y - p1.Y)*(long)(pp1.X - pp0.X) + + (long)(p1.X - p0.X)*(long)(pp1.Y - pp0.Y) ); + Point c; + switch(Geometry.findIntersection(p0, p1, pp0, pp1, out c)) { + case Geometry.IntersectionType.Cross: count += 2*d; break; - case IntersectionType.Touch_b0: + case Geometry.IntersectionType.Touch_b0: count += d; break; - case IntersectionType.Touch_b1: + case Geometry.IntersectionType.Touch_b1: count -= d; break; default: @@ -813,21 +442,21 @@ namespace Contours { bool isContourInverted(Contour contour) { Link first = contour.forward.getFirst(); - VectorInt p0 = first.position.getParent().toVectorInt(); - VectorInt p1 = first.contour.getNext().position.getParent().toVectorInt(); - makeLongestLine(p0, ref p1); - return countContourIntersections(contour, p0, p1) < 0; + Point p0 = first.position.getParent().point; + Point p1 = first.contour.getNext().position.getParent().point; + Geometry.makeLongestLine(p0, ref p1); + return countIntersectionsWithContour(p0, p1, contour) < 0; } bool isContourInside(Contour inner, Contour outer) { Link first = inner.forward.getFirst(); - VectorInt p0 = first.position.getParent().toVectorInt(); - VectorInt p1 = first.contour.getNext().position.getParent().toVectorInt(); - makeLongestLine(p0, ref p1); - return countContourIntersections(outer, p0, p1) != 0; + Point p0 = first.position.getParent().point; + Point p1 = first.contour.getNext().position.getParent().point; + Geometry.makeLongestLine(p0, ref p1); + return countIntersectionsWithContour(p0, p1, outer) != 0; } - void sortChilds(Contour contour, Contour parent) { + void organizeChildContours(Contour contour, Contour parent) { // set parent contour.parent = parent; @@ -838,58 +467,55 @@ namespace Contours { // sub-calls foreach(Contour c in contour.childs) - sortChilds(c, contour); + organizeChildContours(c, contour); } - void sortContours() { + void buildContoursHierarhy(bool simple) { // calculate directions of contours - for(Contour contourA = contours.getFirst(); contourA != null; contourA = contourA.shape.getNext()) { - contourA.inverted = isContourInverted(contourA); - rootContours.Add(contourA); - } + foreach(Contour contour in contours) + contour.inverted = isContourInverted(contour); // find childs - for(Contour contourA = contours.getFirst(); contourA != null; contourA = contourA.shape.getNext()) { - for(Contour contourB = contourA.shape.getNextLinear(); contourB != null; contourB = contourB.shape.getNext()) { - if (isContourInside(contourA, contourB)) { - contourB.childs.Add(contourA); - rootContours.Remove(contourA); + rootContours.AddRange(contours); + for(int i = 0; i < contours.Count; ++i) { + for(int j = 0; j < contours.Count; ++j) { + if (isContourInside(contours[i], contours[j])) { + contours[j].childs.Add(contours[i]); + rootContours.Remove(contours[i]); } else - if (isContourInside(contourA, contourB)) { - contourA.childs.Add(contourB); - rootContours.Remove(contourB); + if (isContourInside(contours[j], contours[i])) { + contours[i].childs.Add(contours[j]); + rootContours.Remove(contours[j]); } } } - // sort childs + // organize childs foreach(Contour c in rootContours) - sortChilds(c, null); - + organizeChildContours(c, null); + + // TODO: if simple then invert invisible contours instead of removing + // remove invisible contours - bool retry = true; - while(retry) { - retry = false; - for(Contour contourA = contours.getFirst(); contourA != null; contourA = contourA.shape.getNext()) { - bool parentInverted = contourA.parent == null || contourA.parent.inverted; - if (parentInverted == contourA.inverted) { - // remove contour - List parentList = contourA.parent == null ? rootContours : contourA.parent.childs; - foreach(Contour c in contourA.childs) - c.parent = contourA.parent; - parentList.AddRange(contourA.childs); - - contourA.parent = null; - contourA.childs.Clear(); - contourA.shape.unlink(); - while(!contourA.backward.empty()) - contourA.forward.getFirst().unlink(); - while(!contourA.backward.empty()) - contourA.forward.getFirst().unlink(); - - retry = true; - break; - } + for(int i = 0; i < contours.Count; ++i) { + bool parentInverted = contours[i].parent == null || contours[i].parent.inverted; + if (parentInverted == contours[i].inverted) { + // remove contour + foreach(Contour c in contours[i].childs) + c.parent = contours[i].parent; + List parentList = contours[i].parent == null + ? rootContours + : contours[i].parent.childs; + parentList.AddRange(contours[i].childs); + + contours[i].parent = null; + contours[i].childs.Clear(); + while(!contours[i].forward.empty()) + removeLink(contours[i].forward.getFirst()); + while(!contours[i].backward.empty()) + removeLink(contours[i].backward.getFirst()); + + contours.RemoveAt(i--); } } @@ -906,14 +532,13 @@ namespace Contours { } } - void optimizeContours() { + void calculate(bool simple) { + resetTraceInformation(); findIntersections(); - unlinkContoursChains(); - sortLinksAtPositions(); optimizePositions(); - traceContours(); - removeEmptyContours(); - sortContours(); + buildContours(); + removeFreeLinks(); + buildContoursHierarhy(simple); removeEmptyPositions(); } }