From c0fac0f5cae13f61d74266d3fe4073e349483ecc Mon Sep 17 00:00:00 2001 From: GaborFarkas Date: Mon, 15 May 2017 22:52:19 +0200 Subject: [PATCH 1/3] Prepare replays for vector tiles --- src/ol/render/webgl/linestringreplay.js | 21 ++++--- src/ol/render/webgl/polygonreplay.js | 81 ++++++++++++++----------- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/src/ol/render/webgl/linestringreplay.js b/src/ol/render/webgl/linestringreplay.js index f126527392..e72ea6c9aa 100644 --- a/src/ol/render/webgl/linestringreplay.js +++ b/src/ol/render/webgl/linestringreplay.js @@ -324,16 +324,19 @@ if (ol.ENABLE_WEBGL) { */ ol.render.webgl.LineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) { var indexCount = this.indices.length; - var lineStringGeometries = multiLineStringGeometry.getLineStrings(); + var ends = multiLineStringGeometry.getEnds(); + ends.unshift(0); + var flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); + var stride = multiLineStringGeometry.getStride(); var i, ii; - for (i = 0, ii = lineStringGeometries.length; i < ii; ++i) { - var flatCoordinates = lineStringGeometries[i].getFlatCoordinates(); - var stride = lineStringGeometries[i].getStride(); - if (this.isValid_(flatCoordinates, 0, flatCoordinates.length, stride)) { - flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length, - stride, -this.origin[0], -this.origin[1]); - this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); + if (ends.length > 1) { + for (i = 1, ii = ends.length; i < ii; ++i) { + if (this.isValid_(flatCoordinates, ends[i - 1], ends[i], stride)) { + var lineString = ol.geom.flat.transform.translate(flatCoordinates, ends[i - 1], ends[i], + stride, -this.origin[0], -this.origin[1]); + this.drawCoordinates_( + lineString, 0, lineString.length, stride); + } } } if (this.indices.length > indexCount) { diff --git a/src/ol/render/webgl/polygonreplay.js b/src/ol/render/webgl/polygonreplay.js index eb228bb554..80809c234e 100644 --- a/src/ol/render/webgl/polygonreplay.js +++ b/src/ol/render/webgl/polygonreplay.js @@ -712,28 +712,33 @@ if (ol.ENABLE_WEBGL) { * @inheritDoc */ ol.render.webgl.PolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) { - var polygons = multiPolygonGeometry.getPolygons(); + var endss = multiPolygonGeometry.getEndss(); var stride = multiPolygonGeometry.getStride(); var currIndex = this.indices.length; var currLineIndex = this.lineStringReplay.getCurrentIndex(); + var flatCoordinates = multiPolygonGeometry.getFlatCoordinates(); var i, ii, j, jj; - for (i = 0, ii = polygons.length; i < ii; ++i) { - var linearRings = polygons[i].getLinearRings(); - if (linearRings.length > 0) { - var flatCoordinates = linearRings[0].getFlatCoordinates(); - flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length, + var start = 0; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + if (ends.length > 0) { + var outerRing = ol.geom.flat.transform.translate(flatCoordinates, start, ends[0], stride, -this.origin[0], -this.origin[1]); - var holes = []; - var holeFlatCoords; - for (j = 1, jj = linearRings.length; j < jj; ++j) { - holeFlatCoords = linearRings[j].getFlatCoordinates(); - holeFlatCoords = ol.geom.flat.transform.translate(holeFlatCoords, 0, holeFlatCoords.length, - stride, -this.origin[0], -this.origin[1]); - holes.push(holeFlatCoords); + if (outerRing.length) { + var holes = []; + var holeFlatCoords; + for (j = 1, jj = ends.length; j < jj; ++j) { + if (ends[j] !== ends[j - 1]) { + holeFlatCoords = ol.geom.flat.transform.translate(flatCoordinates, ends[j - 1], + ends[j], stride, -this.origin[0], -this.origin[1]); + holes.push(holeFlatCoords); + } + } + this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); + this.drawCoordinates_(outerRing, holes, stride); } - this.lineStringReplay.drawPolygonCoordinates(flatCoordinates, holes, stride); - this.drawCoordinates_(flatCoordinates, holes, stride); } + start = ends[ends.length - 1]; } if (this.indices.length > currIndex) { this.startIndices.push(currIndex); @@ -753,30 +758,34 @@ if (ol.ENABLE_WEBGL) { * @inheritDoc */ ol.render.webgl.PolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) { - var linearRings = polygonGeometry.getLinearRings(); + var ends = polygonGeometry.getEnds(); var stride = polygonGeometry.getStride(); - if (linearRings.length > 0) { - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(this.indices.length); - this.state_.changed = false; - } - this.lineStringReplay.setPolygonStyle(feature); - - var flatCoordinates = linearRings[0].getFlatCoordinates(); - flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length, + if (ends.length > 0) { + var flatCoordinates = polygonGeometry.getFlatCoordinates(); + var outerRing = ol.geom.flat.transform.translate(flatCoordinates, 0, ends[0], stride, -this.origin[0], -this.origin[1]); - var holes = []; - var i, ii, holeFlatCoords; - for (i = 1, ii = linearRings.length; i < ii; ++i) { - holeFlatCoords = linearRings[i].getFlatCoordinates(); - holeFlatCoords = ol.geom.flat.transform.translate(holeFlatCoords, 0, holeFlatCoords.length, - stride, -this.origin[0], -this.origin[1]); - holes.push(holeFlatCoords); + if (outerRing.length) { + var holes = []; + var i, ii, holeFlatCoords; + for (i = 1, ii = ends.length; i < ii; ++i) { + if (ends[i] !== ends[i - 1]) { + holeFlatCoords = ol.geom.flat.transform.translate(flatCoordinates, ends[i - 1], + ends[i], stride, -this.origin[0], -this.origin[1]); + holes.push(holeFlatCoords); + } + } + + this.startIndices.push(this.indices.length); + this.startIndicesFeature.push(feature); + if (this.state_.changed) { + this.styleIndices_.push(this.indices.length); + this.state_.changed = false; + } + this.lineStringReplay.setPolygonStyle(feature); + + this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); + this.drawCoordinates_(outerRing, holes, stride); } - this.lineStringReplay.drawPolygonCoordinates(flatCoordinates, holes, stride); - this.drawCoordinates_(flatCoordinates, holes, stride); } }; From bcda41b508949be45741e408aa7f5d2f62935777 Mon Sep 17 00:00:00 2001 From: GaborFarkas Date: Sun, 21 May 2017 13:44:28 +0200 Subject: [PATCH 2/3] Do not bridge holes outside of outer ring --- src/ol/render/webgl/polygonreplay.js | 25 ++++++++++++++++++++----- src/ol/structs/rbush.js | 11 +++++++++++ test/spec/ol/structs/rbush.test.js | 25 +++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/ol/render/webgl/polygonreplay.js b/src/ol/render/webgl/polygonreplay.js index 80809c234e..7c442f8a0f 100644 --- a/src/ol/render/webgl/polygonreplay.js +++ b/src/ol/render/webgl/polygonreplay.js @@ -88,17 +88,33 @@ if (ol.ENABLE_WEBGL) { for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) { var holeList = { list: new ol.structs.LinkedList(), - maxX: undefined + maxX: undefined, + rtree: new ol.structs.RBush() }; holeLists.push(holeList); holeList.maxX = this.processFlatCoordinates_(holeFlatCoordinates[i], - stride, holeList.list, rtree, false); + stride, holeList.list, holeList.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, rtree); + var currList = holeLists[i].list; + var start = currList.firstItem(); + var currItem = start; + var intersection; + do { + if (this.getIntersections_(currItem, rtree).length) { + intersection = true; + break; + } + currItem = currList.nextItem(); + } while (start !== currItem); + if (!intersection) { + this.classifyPoints_(currList, holeLists[i].rtree, true); + this.bridgeHole_(currList, holeLists[i].maxX, outerRing, maxX, rtree); + rtree.concat(holeLists[i].rtree); + } } } this.classifyPoints_(outerRing, rtree, false); @@ -215,7 +231,6 @@ if (ol.ENABLE_WEBGL) { */ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, list, listMaxX, rtree) { - this.classifyPoints_(hole, rtree, true); var seg = hole.firstItem(); while (seg.p1.x !== holeMaxX) { seg = hole.nextItem(); @@ -761,7 +776,7 @@ if (ol.ENABLE_WEBGL) { var ends = polygonGeometry.getEnds(); var stride = polygonGeometry.getStride(); if (ends.length > 0) { - var flatCoordinates = polygonGeometry.getFlatCoordinates(); + var flatCoordinates = polygonGeometry.getFlatCoordinates().map(Number); var outerRing = ol.geom.flat.transform.translate(flatCoordinates, 0, ends[0], stride, -this.origin[0], -this.origin[1]); if (outerRing.length) { diff --git a/src/ol/structs/rbush.js b/src/ol/structs/rbush.js index 0828832452..f802507dad 100644 --- a/src/ol/structs/rbush.js +++ b/src/ol/structs/rbush.js @@ -215,3 +215,14 @@ ol.structs.RBush.prototype.getExtent = function(opt_extent) { var data = this.rbush_.data; return ol.extent.createOrUpdate(data.minX, data.minY, data.maxX, data.maxY, opt_extent); }; + + +/** + * @param {ol.structs.RBush} rbush R-Tree. + */ +ol.structs.RBush.prototype.concat = function(rbush) { + this.rbush_.load(rbush.rbush_.all()); + for (var i in rbush.items_) { + this.items_[i | 0] = rbush.items_[i | 0]; + } +}; diff --git a/test/spec/ol/structs/rbush.test.js b/test/spec/ol/structs/rbush.test.js index 1e9741ce83..2ff46ab541 100644 --- a/test/spec/ol/structs/rbush.test.js +++ b/test/spec/ol/structs/rbush.test.js @@ -313,4 +313,29 @@ describe('ol.structs.RBush', function() { }); + describe('#concat', function() { + + it('concatenates two RBush objects', function() { + var obj1 = {}; + var obj2 = {}; + var rBush2 = new ol.structs.RBush(); + rBush.insert([0, 0, 1, 1], obj1); + rBush2.insert([0, 0, 2, 2], obj2); + rBush.concat(rBush2); + expect(rBush.getExtent()).to.eql([0, 0, 2, 2]); + expect(rBush.getAll().length).to.be(2); + }); + + it('preserves the concatenated object\'s references', function() { + var obj1 = {}; + var obj2 = {}; + var rBush2 = new ol.structs.RBush(); + rBush.insert([0, 0, 1, 1], obj1); + rBush2.insert([0, 0, 2, 2], obj2); + rBush.concat(rBush2); + rBush.update([0, 0, 3, 3], obj2); + expect(rBush.getExtent()).to.eql([0, 0, 3, 3]); + }); + }); + }); From 7ea8cf5fb322af2878a7362539d5bc16758cb95e Mon Sep 17 00:00:00 2001 From: GaborFarkas Date: Mon, 29 May 2017 15:51:11 +0200 Subject: [PATCH 3/3] Don't introduce new self-intersections. Fixes #6823 --- src/ol/render/webgl/polygonreplay.js | 57 +++++++++++++++++----------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/ol/render/webgl/polygonreplay.js b/src/ol/render/webgl/polygonreplay.js index 7c442f8a0f..d0f885234b 100644 --- a/src/ol/render/webgl/polygonreplay.js +++ b/src/ol/render/webgl/polygonreplay.js @@ -96,7 +96,7 @@ if (ol.ENABLE_WEBGL) { stride, holeList.list, holeList.rtree, false); } holeLists.sort(function(a, b) { - return b.maxX - a.maxX; + return b.maxX[0] === a.maxX[0] ? a.maxX[1] - b.maxX[1] : b.maxX[0] - a.maxX[0]; }); for (i = 0; i < holeLists.length; ++i) { var currList = holeLists[i].list; @@ -112,12 +112,15 @@ if (ol.ENABLE_WEBGL) { } while (start !== currItem); if (!intersection) { this.classifyPoints_(currList, holeLists[i].rtree, true); - this.bridgeHole_(currList, holeLists[i].maxX, outerRing, maxX, rtree); - rtree.concat(holeLists[i].rtree); + if (this.bridgeHole_(currList, holeLists[i].maxX[0], outerRing, maxX[0], rtree)) { + rtree.concat(holeLists[i].rtree); + this.classifyPoints_(outerRing, rtree, false); + } } } + } else { + this.classifyPoints_(outerRing, rtree, false); } - this.classifyPoints_(outerRing, rtree, false); this.triangulate_(outerRing, rtree); }; @@ -130,13 +133,13 @@ if (ol.ENABLE_WEBGL) { * @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. + * @return {Array.} X and Y coords of maximum X value. */ ol.render.webgl.PolygonReplay.prototype.processFlatCoordinates_ = function( flatCoordinates, stride, list, rtree, clockwise) { var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(flatCoordinates, 0, flatCoordinates.length, stride); - var i, ii, maxX; + var i, ii, maxXX, maxXY; var n = this.vertices.length / 2; /** @type {ol.WebglPolygonVertex} */ var start; @@ -149,13 +152,17 @@ if (ol.ENABLE_WEBGL) { if (clockwise === isClockwise) { start = this.createPoint_(flatCoordinates[0], flatCoordinates[1], n++); p0 = start; - maxX = flatCoordinates[0]; + maxXX = flatCoordinates[0]; + maxXY = flatCoordinates[1]; for (i = stride, ii = flatCoordinates.length; i < ii; i += stride) { p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); segments.push(this.insertItem_(p0, p1, list)); 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)]); - maxX = flatCoordinates[i] > maxX ? flatCoordinates[i] : maxX; + if (flatCoordinates[i] > maxXX) { + maxXX = flatCoordinates[i]; + maxXY = flatCoordinates[i + 1]; + } p0 = p1; } segments.push(this.insertItem_(p1, start, list)); @@ -165,13 +172,17 @@ if (ol.ENABLE_WEBGL) { var end = flatCoordinates.length - stride; start = this.createPoint_(flatCoordinates[end], flatCoordinates[end + 1], n++); p0 = start; - maxX = flatCoordinates[end]; + maxXX = flatCoordinates[end]; + maxXY = flatCoordinates[end + 1]; for (i = end - stride, ii = 0; i >= ii; i -= stride) { p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); segments.push(this.insertItem_(p0, p1, list)); 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)]); - maxX = flatCoordinates[i] > maxX ? flatCoordinates[i] : maxX; + if (flatCoordinates[i] > maxXX) { + maxXX = flatCoordinates[i]; + maxXY = flatCoordinates[i + 1]; + } p0 = p1; } segments.push(this.insertItem_(p1, start, list)); @@ -180,7 +191,7 @@ if (ol.ENABLE_WEBGL) { } rtree.load(extents, segments); - return maxX; + return [maxXX, maxXY]; }; @@ -228,6 +239,7 @@ if (ol.ENABLE_WEBGL) { * @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. + * @return {boolean} Bridging was successful. */ ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, list, listMaxX, rtree) { @@ -247,19 +259,18 @@ if (ol.ENABLE_WEBGL) { 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.reflex === undefined) { - var intersection = this.calculateIntersection_(p1, p2, currSeg.p0, - currSeg.p1, true); - var dist = Math.abs(p1.x - intersection[0]); - if (dist < minDist) { - minDist = dist; - p5 = {x: intersection[0], y: intersection[1], i: -1}; - seg = currSeg; - } + var intersection = this.calculateIntersection_(p1, p2, currSeg.p0, + currSeg.p1, true); + var dist = Math.abs(p1.x - intersection[0]); + if (dist < minDist && ol.render.webgl.triangleIsCounterClockwise(p1.x, p1.y, + currSeg.p0.x, currSeg.p0.y, currSeg.p1.x, currSeg.p1.y) !== undefined) { + minDist = dist; + p5 = {x: intersection[0], y: intersection[1], i: -1}; + seg = currSeg; } } if (minDist === Infinity) { - return; + return false; } bestPoint = seg.p1; @@ -279,7 +290,7 @@ if (ol.ENABLE_WEBGL) { } seg = list.firstItem(); - while (seg.p1 !== bestPoint) { + while (seg.p1.x !== bestPoint.x || seg.p1.y !== bestPoint.y) { seg = list.nextItem(); } @@ -293,6 +304,8 @@ if (ol.ENABLE_WEBGL) { seg.p1 = p1Bridge; hole.setFirstItem(); list.concat(hole); + + return true; };