diff --git a/src/ol/render/webgl/imagereplay/index.js b/src/ol/render/webgl/imagereplay/index.js index 153ea06517..754213f15e 100644 --- a/src/ol/render/webgl/imagereplay/index.js +++ b/src/ol/render/webgl/imagereplay/index.js @@ -1701,11 +1701,6 @@ ol.render.webgl.PolygonReplay = function(tolerance, maxExtent) { changed: false }; - /** - * @private - */ - this.rtree_ = new ol.structs.RBush(); - }; ol.inherits(ol.render.webgl.PolygonReplay, ol.render.webgl.Replay); @@ -1721,8 +1716,9 @@ ol.render.webgl.PolygonReplay.prototype.drawCoordinates_ = function( flatCoordinates, holeFlatCoordinates, stride) { // Triangulate the polygon var outerRing = new ol.structs.LinkedList(); + var rtree = new ol.structs.RBush(); // Initialize the outer ring - var maxX = this.processFlatCoordinates_(flatCoordinates, stride, outerRing, true); + var maxX = this.processFlatCoordinates_(flatCoordinates, stride, outerRing, rtree, true); // Eliminate holes, if there are any if (holeFlatCoordinates.length) { @@ -1735,20 +1731,17 @@ ol.render.webgl.PolygonReplay.prototype.drawCoordinates_ = function( }; holeLists.push(holeList); holeList.maxX = this.processFlatCoordinates_(holeFlatCoordinates[i], - stride, holeList.list, false); + stride, holeList.list, rtree, false); } holeLists.sort(function(a, b) { return b.maxX - a.maxX; }); for (i = 0; i < holeLists.length; ++i) { - this.bridgeHole_(holeLists[i].list, holeLists[i].maxX, outerRing, maxX); + this.bridgeHole_(holeLists[i].list, holeLists[i].maxX, outerRing, maxX, rtree); } } - this.classifyPoints_(outerRing, false); - this.triangulate_(outerRing); - - // We clear the R-Tree here, because hit detection does not call finish() - this.rtree_.clear(); + this.classifyPoints_(outerRing, rtree, false); + this.triangulate_(outerRing, rtree); }; @@ -1758,11 +1751,12 @@ ol.render.webgl.PolygonReplay.prototype.drawCoordinates_ = function( * @param {Array.} flatCoordinates Flat coordinates. * @param {number} stride Stride. * @param {ol.structs.LinkedList} list Linked list. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. * @param {boolean} clockwise Coordinate order should be clockwise. * @return {number} Maximum X value. */ ol.render.webgl.PolygonReplay.prototype.processFlatCoordinates_ = function( - flatCoordinates, stride, list, clockwise) { + flatCoordinates, stride, list, rtree, clockwise) { var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(flatCoordinates, 0, flatCoordinates.length, stride); var i, ii, maxX; @@ -1807,7 +1801,7 @@ ol.render.webgl.PolygonReplay.prototype.processFlatCoordinates_ = function( extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)]); } - this.rtree_.load(extents, segments); + rtree.load(extents, segments); return maxX; }; @@ -1817,10 +1811,11 @@ ol.render.webgl.PolygonReplay.prototype.processFlatCoordinates_ = function( * Classifies the points of a polygon list as convex, reflex. Removes collinear vertices. * @private * @param {ol.structs.LinkedList} list Polygon ring. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. * @param {boolean} ccw The orientation of the polygon is counter-clockwise. * @return {boolean} There were reclassified points. */ -ol.render.webgl.PolygonReplay.prototype.classifyPoints_ = function(list, ccw) { +ol.render.webgl.PolygonReplay.prototype.classifyPoints_ = function(list, rtree, ccw) { var start = list.firstItem(); var s0 = start; var s1 = list.nextItem(); @@ -1831,7 +1826,7 @@ ol.render.webgl.PolygonReplay.prototype.classifyPoints_ = function(list, ccw) { ol.render.webgl.triangleIsCounterClockwise(s0.p0.x, s0.p0.y, s0.p1.x, s0.p1.y, s1.p1.x, s1.p1.y); if (reflex === undefined) { - this.removeItem_(s0, s1, list); + this.removeItem_(s0, s1, list, rtree); pointsReclassified = true; if (s1 === start) { start = list.getNextItem(); @@ -1855,9 +1850,10 @@ ol.render.webgl.PolygonReplay.prototype.classifyPoints_ = function(list, ccw) { * @param {number} holeMaxX Maximum X value of the hole. * @param {ol.structs.LinkedList} list Linked list of the polygon. * @param {number} listMaxX Maximum X value of the polygon. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. */ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, - list, listMaxX) { + list, listMaxX, rtree) { var seg = hole.firstItem(); while (seg.p1.x !== holeMaxX) { seg = hole.nextItem(); @@ -1871,7 +1867,7 @@ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, /** @type {ol.WebglPolygonVertex} */ var p5; - var intersectingSegments = this.getIntersections_({p0: p1, p1: p2}, true); + var intersectingSegments = this.getIntersections_({p0: p1, p1: p2}, rtree, true); for (i = 0, ii = intersectingSegments.length; i < ii; ++i) { var currSeg = intersectingSegments[i]; if (currSeg.p0 !== p1 && currSeg.p1 !== p1) { @@ -1887,7 +1883,7 @@ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, } bestPoint = seg.p1; - var pointsInTriangle = this.getPointsInTriangle_(p1, p5, seg.p1); + var pointsInTriangle = this.getPointsInTriangle_(p1, p5, seg.p1, rtree); if (pointsInTriangle.length) { var theta = Infinity; for (i = 0, ii = pointsInTriangle.length; i < ii; ++i) { @@ -1910,8 +1906,8 @@ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, var p1Bridge = {x: seg.p1.x, y: seg.p1.y, i: seg.p1.i, reflex: undefined}; hole.getNextItem().p0 = p0Bridge; - this.insertItem_(p1, seg.p1, hole, true); - this.insertItem_(p1Bridge, p0Bridge, hole, true); + this.insertItem_(p1, seg.p1, hole, rtree); + this.insertItem_(p1Bridge, p0Bridge, hole, rtree); seg.p1 = p1Bridge; hole.setFirstItem(); list.concat(hole); @@ -1921,17 +1917,18 @@ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, /** * @private * @param {ol.structs.LinkedList} list Linked list of the polygon. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. */ -ol.render.webgl.PolygonReplay.prototype.triangulate_ = function(list) { +ol.render.webgl.PolygonReplay.prototype.triangulate_ = function(list, rtree) { var ccw = false; - var simple = this.isSimple_(list); + var simple = this.isSimple_(list, rtree); var pass = 0; // Start clipping ears while (list.getLength() > 3) { if (simple) { - if (!this.clipEars_(list, simple, ccw)) { - if (!this.classifyPoints_(list, ccw)) { + if (!this.clipEars_(list, rtree, simple, ccw)) { + if (!this.classifyPoints_(list, rtree, ccw)) { // We have the wrongly oriented remains of a self-intersecting polygon. pass++; if (pass > 1) { @@ -1939,18 +1936,19 @@ ol.render.webgl.PolygonReplay.prototype.triangulate_ = function(list) { break; } ccw = !ccw; - this.classifyPoints_(list, ccw); + this.classifyPoints_(list, rtree, ccw); } } } else { - if (!this.clipEars_(list, simple, ccw)) { + if (!this.clipEars_(list, rtree, simple, ccw)) { // We ran out of ears, try to reclassify. - if (!this.classifyPoints_(list, ccw)) { + if (!this.classifyPoints_(list, rtree, ccw)) { // We have a bad polygon, try to resolve local self-intersections. - if (!this.resolveLocalSelfIntersections_(list)) { - simple = this.isSimple_(list); + if (!this.resolveLocalSelfIntersections_(list, rtree)) { + simple = this.isSimple_(list, rtree); if (!simple) { // We have a really bad polygon, try more time consuming methods. + this.splitPolygon_(list, rtree); break; } } @@ -1970,11 +1968,12 @@ ol.render.webgl.PolygonReplay.prototype.triangulate_ = function(list) { /** * @private * @param {ol.structs.LinkedList} list Linked list of the polygon. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. * @param {boolean} simple The polygon is simple. * @param {boolean} ccw Orientation of the polygon is counter-clockwise. * @return {boolean} There were processed ears. */ -ol.render.webgl.PolygonReplay.prototype.clipEars_ = function(list, simple, ccw) { +ol.render.webgl.PolygonReplay.prototype.clipEars_ = function(list, rtree, simple, ccw) { var numIndices = this.indices_.length; var start = list.firstItem(); var s0 = list.getPrevItem(); @@ -1991,8 +1990,8 @@ ol.render.webgl.PolygonReplay.prototype.clipEars_ = function(list, simple, ccw) // We might have a valid ear var diagonalIsInside = ccw ? this.diagonalIsInside_(s3.p1, p2, p1, p0, s0.p0) : this.diagonalIsInside_(s0.p0, p0, p1, p2, s3.p1); - if ((simple || this.getIntersections_({p0: p0, p1: p2}).length === 0) && - diagonalIsInside && this.getPointsInTriangle_(p0, p1, p2, true).length === 0) { + if ((simple || this.getIntersections_({p0: p0, p1: p2}, rtree).length === 0) && + diagonalIsInside && this.getPointsInTriangle_(p0, p1, p2, rtree, true).length === 0) { //The diagonal is completely inside the polygon if (p0.reflex === false || p2.reflex === false || ol.geom.flat.orient.linearRingIsClockwise([s0.p0.x, s0.p0.y, p0.x, @@ -2001,7 +2000,7 @@ ol.render.webgl.PolygonReplay.prototype.clipEars_ = function(list, simple, ccw) this.indices_[numIndices++] = p0.i; this.indices_[numIndices++] = p1.i; this.indices_[numIndices++] = p2.i; - this.removeItem_(s1, s2, list); + this.removeItem_(s1, s2, list, rtree); if (s2 === start) { start = s3; } @@ -2023,10 +2022,11 @@ ol.render.webgl.PolygonReplay.prototype.clipEars_ = function(list, simple, ccw) /** * @private * @param {ol.structs.LinkedList} list Linked list of the polygon. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. * @return {boolean} There were resolved intersections. */ ol.render.webgl.PolygonReplay.prototype.resolveLocalSelfIntersections_ = function( - list) { + list, rtree) { var start = list.firstItem(); list.nextItem(); var s0 = start; @@ -2043,12 +2043,12 @@ ol.render.webgl.PolygonReplay.prototype.resolveLocalSelfIntersections_ = functio var seg = list.prevItem(); list.removeItem(); - this.rtree_.remove(seg); + rtree.remove(seg); s0.p1 = p; s1.p0 = p; - this.rtree_.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), + rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); - this.rtree_.update([Math.min(s1.p0.x, s1.p1.x), Math.min(s1.p0.y, s1.p1.y), + rtree.update([Math.min(s1.p0.x, s1.p1.x), Math.min(s1.p0.y, s1.p1.y), Math.max(s1.p0.x, s1.p1.x), Math.max(s1.p0.y, s1.p1.y)], s1); this.indices_[numIndices++] = seg.p0.i; this.indices_[numIndices++] = seg.p1.i; @@ -2070,13 +2070,14 @@ ol.render.webgl.PolygonReplay.prototype.resolveLocalSelfIntersections_ = functio /** * @private * @param {ol.structs.LinkedList} list Linked list of the polygon. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. * @return {boolean} The polygon is simple. */ -ol.render.webgl.PolygonReplay.prototype.isSimple_ = function(list) { +ol.render.webgl.PolygonReplay.prototype.isSimple_ = function(list, rtree) { var start = list.firstItem(); var seg = start; do { - if (this.getIntersections_(seg).length) { + if (this.getIntersections_(seg, rtree).length) { return false; } seg = list.nextItem(); @@ -2085,6 +2086,50 @@ ol.render.webgl.PolygonReplay.prototype.isSimple_ = function(list) { }; +/** + * @private + * @param {ol.structs.LinkedList} list Linked list of the polygon. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. + */ +ol.render.webgl.PolygonReplay.prototype.splitPolygon_ = function(list, rtree) { + var start = list.firstItem(); + var s0 = start; + do { + var intersections = this.getIntersections_(s0, rtree); + if (intersections.length) { + var s1 = intersections[0]; + var n = this.vertices_.length / 2; + var intersection = this.calculateIntersection_(s0.p0, + s0.p1, s1.p0, s1.p1); + var p = this.createPoint_(intersection[0], intersection[1], n); + var newPolygon = new ol.structs.LinkedList(); + var newRtree = new ol.structs.RBush(); + this.insertItem_(p, s0.p1, newPolygon, newRtree); + s0.p1 = p; + rtree.update([Math.min(s0.p0.x, p.x), Math.min(s0.p0.y, p.y), + Math.max(s0.p0.x, p.x), Math.max(s0.p0.y, p.y)], s0); + var currItem = list.nextItem(); + while (currItem !== s1) { + this.insertItem_(currItem.p0, currItem.p1, newPolygon, newRtree); + rtree.remove(currItem); + list.removeItem(); + currItem = list.getCurrItem(); + } + this.insertItem_(s1.p0, p, newPolygon, newRtree); + s1.p0 = p; + rtree.update([Math.min(s1.p1.x, p.x), Math.min(s1.p1.y, p.y), + Math.max(s1.p1.x, p.x), Math.max(s1.p1.y, p.y)], s1); + this.classifyPoints_(list, rtree, false); + this.triangulate_(list, rtree); + this.classifyPoints_(newPolygon, newRtree, false); + this.triangulate_(newPolygon, newRtree); + break; + } + s0 = list.nextItem(); + } while (s0 !== start); +}; + + /** * @private * @param {number} x X coordinate. @@ -2112,8 +2157,8 @@ ol.render.webgl.PolygonReplay.prototype.createPoint_ = function(x, y, i) { * @param {ol.WebglPolygonVertex} p0 First point of segment. * @param {ol.WebglPolygonVertex} p1 Second point of segment. * @param {ol.structs.LinkedList} list Polygon ring. - * @param {boolean=} opt_rtree Insert the segment into the R-Tree. - * @return {Object.} segment. + * @param {ol.structs.RBush=} opt_rtree Insert the segment into the R-Tree. + * @return {ol.WebglPolygonSegment} segment. */ ol.render.webgl.PolygonReplay.prototype.insertItem_ = function(p0, p1, list, opt_rtree) { var seg = { @@ -2122,7 +2167,7 @@ ol.render.webgl.PolygonReplay.prototype.insertItem_ = function(p0, p1, list, opt }; list.insertItem(seg); if (opt_rtree) { - this.rtree_.insert([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), + opt_rtree.insert([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)], seg); } return seg; @@ -2131,15 +2176,16 @@ ol.render.webgl.PolygonReplay.prototype.insertItem_ = function(p0, p1, list, opt /** * @private - * @param {Object.} s0 Segment before the remove candidate. - * @param {Object.} s1 Remove candidate segment. + * @param {ol.WebglPolygonSegment} s0 Segment before the remove candidate. + * @param {ol.WebglPolygonSegment} s1 Remove candidate segment. * @param {ol.structs.LinkedList} list Polygon ring. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. */ -ol.render.webgl.PolygonReplay.prototype.removeItem_ = function(s0, s1, list) { +ol.render.webgl.PolygonReplay.prototype.removeItem_ = function(s0, s1, list, rtree) { list.removeItem(); s0.p1 = s1.p1; - this.rtree_.remove(s1); - this.rtree_.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), + rtree.remove(s1); + rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); }; @@ -2149,14 +2195,15 @@ ol.render.webgl.PolygonReplay.prototype.removeItem_ = function(s0, s1, list) { * @param {ol.WebglPolygonVertex} p0 First point. * @param {ol.WebglPolygonVertex} p1 Second point. * @param {ol.WebglPolygonVertex} p2 Third point. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. * @param {boolean=} opt_reflex Only include reflex points. - * @return {Array.>} Points in the triangle. + * @return {Array.} Points in the triangle. */ ol.render.webgl.PolygonReplay.prototype.getPointsInTriangle_ = function(p0, p1, - p2, opt_reflex) { + p2, rtree, opt_reflex) { var i, ii, j, p; var result = []; - var segmentsInExtent = this.rtree_.getInExtent([Math.min(p0.x, p1.x, p2.x), + var segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x, p2.x), Math.min(p0.y, p1.y, p2.y), Math.max(p0.x, p1.x, p2.x), Math.max(p0.y, p1.y, p2.y)]); for (i = 0, ii = segmentsInExtent.length; i < ii; ++i) { @@ -2178,14 +2225,15 @@ ol.render.webgl.PolygonReplay.prototype.getPointsInTriangle_ = function(p0, p1, /** * @private - * @param {Object.} segment Segment. + * @param {ol.WebglPolygonSegment} segment Segment. + * @param {ol.structs.RBush} rtree R-Tree of the polygon. * @param {boolean=} opt_touch Touching segments should be considered an intersection. - * @return {Array.>} Intersecting segments. + * @return {Array.} Intersecting segments. */ -ol.render.webgl.PolygonReplay.prototype.getIntersections_ = function(segment, opt_touch) { +ol.render.webgl.PolygonReplay.prototype.getIntersections_ = function(segment, rtree, opt_touch) { var p0 = segment.p0; var p1 = segment.p1; - var segmentsInExtent = this.rtree_.getInExtent([Math.min(p0.x, p1.x), + var segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)]); var result = []; var i, ii; diff --git a/src/ol/typedefs.js b/src/ol/typedefs.js index 5016d782b0..68782fbf3c 100644 --- a/src/ol/typedefs.js +++ b/src/ol/typedefs.js @@ -694,6 +694,12 @@ ol.ViewAnimation; ol.WebglBufferCacheEntry; +/** + * @typedef {{p0: ol.WebglPolygonVertex, + * p1: ol.WebglPolygonVertex}} + */ +ol.WebglPolygonSegment; + /** * @typedef {{x: number, * y: number,