internal static TinyQueue divideSegment(SweepEvent se, Point p, TinyQueue queue) { var r = new SweepEvent(p, false, se, se.isSubject); var l = new SweepEvent(p, true, se.otherEvent, se.isSubject); if (equals(se.point, se.otherEvent.point)) { MessageBox.Show("what is that?" + se.ToString()); } r.contourId = l.contourId = se.contourId; // avoid a rounding error. The left _event would be processed after the right _event if (compareEvents(l, se.otherEvent) > 0) { se.otherEvent.left = true; l.left = false; } // avoid a rounding error. The left _event would be processed after the right _event // if (compareEvents(se, r) > 0) {} se.otherEvent.otherEvent = l; se.otherEvent = r; queue.push(l); queue.push(r); return(queue); }
internal static TinyQueue fillQueue(Point[][][] subject, Point[][][] clipping, double[] sbbox, double[] cbbox) { TinyQueue eventQueue = new TinyQueue(null, compareEvents); Point[][] polygonSet; bool isExteriorRing; int len = subject.GetLength(0); for (int i = 0; i < len; i++) { polygonSet = subject[i]; int jj = polygonSet.GetLength(0); for (int j = 0; j < jj; j++) { isExteriorRing = (j == 0); if (isExteriorRing) { contourId++; } processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing); } } for (int i = 0, ii = clipping.GetLength(0); i < ii; i++) { polygonSet = clipping[i]; int jj = polygonSet.GetLength(0); for (int j = 0; j < jj; j++) { isExteriorRing = (j == 0); if (isExteriorRing) { contourId++; } processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing); } } return(eventQueue); }
internal static void processPolygon(Point[] contourOrHole, bool isSubject, int depth, TinyQueue queue, double[] bbox, bool isExteriorRing) { int len = contourOrHole.GetLength(0) - 1; for (int i = 0; i < len; i++) { processSegment(contourOrHole[i], contourOrHole[i + 1], isSubject, depth + 1, queue, bbox, isExteriorRing); } }
///* eslint-disable no-unused-vars, no-debugger, no-undef */ //function iteratorEquals(it1, it2) { // return it1._cursor === it2._cursor; //} //function _renderSweepLine(sweepLine, pos, _event) //{ //var map = window.map; //if (!map) return; //if (window.sws) window.sws.forEach(function (p) { //map.removeLayer(p); //}); //window.sws = []; //sweepLine.forEach(function (e) { //var poly = L.polyline([ // e.point.slice().reverse(), // e.otherEvent.point.slice().reverse() //], {color: 'green'}).addTo(map); //window.sws.push(poly); //}); //if (window.vt) map.removeLayer(window.vt); //var v = pos.slice(); //var b = map.getBounds(); //window.vt = L.polyline([ //[b.getNorth(), v[0]], //[b.getSouth(), v[0]] //], {color: 'green', weight: 1}).addTo(map); //if (window.ps) map.removeLayer(window.ps); //window.ps = L.polyline([ //_event.point.slice().reverse(), //_event.otherEvent.point.slice().reverse() //], {color: 'black', weight: 9, opacity: 0.4}).addTo(map); //debugger; //} ///* eslint-enable no-unused-vars, no-debugger, no-undef */ internal static List <SweepEvent> subdivideSegments(TinyQueue eventQueue, Point[][][] subject, Point[][][] clipping, double[] sbbox, double[] cbbox, Operation operation) { RedBlackTree sweepLine = new RedBlackTree(_comp_Segm); List <SweepEvent> sortedEvents = new List <SweepEvent>(); double rightbound = Math.Min(sbbox[2], cbbox[2]); RedBlackTreeIterator prev, next; SweepEvent prevEvent, prevprevEvent; while (eventQueue.length > 0) { SweepEvent _event = eventQueue.pop(); sortedEvents.Add(_event); // optimization by bboxes for intersection and difference goes here - коммент оригинала if ((operation == Operation.INTERSECTION && _event.point.X > rightbound) || (operation == Operation.DIFFERENCE && _event.point.X > sbbox[2])) { break; } if (_event.left) { sweepLine = sweepLine.insert(_event, _event); //_renderSweepLine(sweepLine, _event.point, _event); - коммент оригинала next = sweepLine.find(_event); prev = sweepLine.find(_event); _event.iterator = sweepLine.find(_event); if (!prev.node.Equals(sweepLine.begin.node)) // ??? (prev.node !== sweepLine.begin) { prev.prev(); } else { prev = sweepLine.begin; prev.prev(); prev.next(); } next.next(); //--- prevEvent = (prev.key || null), prevprevEvent; prevEvent = (SweepEvent)Convert.ChangeType(prev.key, typeof(SweepEvent)); computeFields(_event, prevEvent, operation); if (next.node != null) { if (possibleIntersection(_event, (SweepEvent)Convert.ChangeType(next.key, typeof(SweepEvent)), eventQueue) == 2) { computeFields(_event, prevEvent, operation); computeFields(_event, (SweepEvent)Convert.ChangeType(next.key, typeof(SweepEvent)), operation); } } if (prev.node != null) { if (possibleIntersection((SweepEvent)Convert.ChangeType(prev.key, typeof(SweepEvent)), _event, eventQueue) == 2) { RedBlackTreeIterator prevprev = sweepLine.find((SweepEvent)Convert.ChangeType(prev.key, typeof(SweepEvent))); if (!prevprev.node.Equals(sweepLine.begin.node)) //prevprev.node != sweepLine.begin { prevprev.prev(); } else { prevprev = sweepLine.find((SweepEvent)Convert.ChangeType(sweepLine.end.key, typeof(SweepEvent))); prevprev.next(); } prevprevEvent = (SweepEvent)Convert.ChangeType(prevprev.key, typeof(SweepEvent)); computeFields(prevEvent, prevprevEvent, operation); computeFields(_event, prevEvent, operation); } } } else { _event = _event.otherEvent; next = sweepLine.find(_event); prev = sweepLine.find(_event); // _renderSweepLine(sweepLine, _event.otherEvent.point, _event); - коммент оригинала if (!(prev != null && next != null)) { continue; } if (!prev.node.Equals(sweepLine.begin.node)) // prev.node != sweepLine.begin { prev.prev(); } else { prev = sweepLine.begin; prev.prev(); prev.next(); } next.next(); sweepLine = sweepLine.remove(_event); // _renderSweepLine(sweepLine, _event.otherEvent.point, _event); - коммент оригинала if (next.node != null && prev.node != null) { if (prev.node.value != null && next.node.value != null) //-- if (typeof prev.node.value != 'undefined' && typeof next.node.value != 'undefined') { possibleIntersection((SweepEvent)Convert.ChangeType(prev.key, typeof(SweepEvent)), (SweepEvent)Convert.ChangeType(next.key, typeof(SweepEvent)), eventQueue); } } } } return(sortedEvents); }
//var max = Math.max; //var min = Math.min; /** * @param {Array<Number>} s1 * @param {Array<Number>} s2 * @param {Boolean} isSubject * @param {Queue} eventQueue * @param {Array<Number>} bbox */ internal static void processSegment(Point s1, Point s2, bool isSubject, int depth, TinyQueue eventQueue, double[] bbox, bool isExteriorRing) { // Possible degenerate condition. // if (equals(s1, s2)) return; var e1 = new SweepEvent(s1, false, null, isSubject); var e2 = new SweepEvent(s2, false, e1, isSubject); e1.otherEvent = e2; e1.contourId = e2.contourId = depth; if (!isExteriorRing) { e1.isExteriorRing = false; e2.isExteriorRing = false; } if (compareEvents(e1, e2) > 0) { e2.left = true; } else { e1.left = true; } bbox[0] = Math.Min(bbox[0], s1.X); bbox[1] = Math.Min(bbox[1], s1.Y); bbox[2] = Math.Max(bbox[2], s1.X); bbox[3] = Math.Max(bbox[3], s1.Y); // Pushing it so the queue is sorted from left to right, with object on the left having the highest priority. eventQueue.push(e1); eventQueue.push(e2); }
internal static int possibleIntersection(SweepEvent se1, SweepEvent se2, TinyQueue queue) { // that disallows self-intersecting polygons, // did cost us half a day, so I'll leave it // out of respect // if (se1.isSubject === se2.isSubject) return; var inter = SegmentIntersection(se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point); var nintersections = inter != null ? inter.Length : 0; if (nintersections == 0) { return(0); // no intersection } // the line segments intersect at an endpoint of both line segments if ((nintersections == 1) && (equals(se1.point, se2.point) || equals(se1.otherEvent.point, se2.otherEvent.point))) { return(0); } if (nintersections == 2 && se1.isSubject == se2.isSubject) { // if(se1.contourId === se2.contourId){ // console.warn('Edges of the same polygon overlap', // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point); // } //throw new Error('Edges of the same polygon overlap'); return(0); } // The line segments associated to se1 and se2 intersect if (nintersections == 1) { // if the intersection point is not an endpoint of se1 if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) { divideSegment(se1, inter[0], queue); } // if the intersection point is not an endpoint of se2 if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) { divideSegment(se2, inter[0], queue); } return(1); } // The line segments associated to se1 and se2 overlap var events = new List <SweepEvent>(); var leftCoincide = false; var rightCoincide = false; if (equals(se1.point, se2.point)) { leftCoincide = true; // linked } else if (compareEvents(se1, se2) == 1) { events.Add(se2); events.Add(se1); } else { events.Add(se1); events.Add(se2); } if (equals(se1.otherEvent.point, se2.otherEvent.point)) { rightCoincide = true; } else if (compareEvents(se1.otherEvent, se2.otherEvent) == 1) { events.Add(se2.otherEvent); events.Add(se1.otherEvent); } else { events.Add(se1.otherEvent); events.Add(se2.otherEvent); } if ((leftCoincide && rightCoincide) || leftCoincide) // both line segments are equal or share the left endpoint { se1.type = EdgeType.NON_CONTRIBUTING; se2.type = (se1.inOut == se2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION; if (leftCoincide && !rightCoincide) { // honestly no idea, but changing events selection from [2, 1] to [0, 1] fixes the overlapping self-intersecting polygons issue divideSegment(events[1].otherEvent, events[0].point, queue); } return(2); } if (rightCoincide) // the line segments share the right endpoint { divideSegment(events[0], events[1].point, queue); return(3); } if (events[0] != events[3].otherEvent) // no line segment includes totally the other one { divideSegment(events[0], events[1].point, queue); divideSegment(events[1], events[2].point, queue); return(3); } // one line segment includes the other one divideSegment(events[0], events[1].point, queue); divideSegment(events[3].otherEvent, events[2].point, queue); return(3); }