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