diff --git a/src/ol/render/canvas/Builder.js b/src/ol/render/canvas/Builder.js index ee7f180e02..9d40e8e2b6 100644 --- a/src/ol/render/canvas/Builder.js +++ b/src/ol/render/canvas/Builder.js @@ -6,7 +6,12 @@ import GeometryType from '../../geom/GeometryType.js'; import Relationship from '../../extent/Relationship.js'; import VectorContext from '../VectorContext.js'; import {asColorLike} from '../../colorlike.js'; -import {buffer, clone, coordinateRelationship} from '../../extent.js'; +import { + buffer, + clone, + containsCoordinate, + coordinateRelationship, +} from '../../extent.js'; import { defaultFillStyle, defaultLineCap, @@ -139,6 +144,28 @@ class CanvasBuilder extends VectorContext { }); } + /** + * @param {Array} flatCoordinates Flat coordinates. + * @param {number} stride Stride. + * @protected + * @return {number} My end + */ + appendFlatPointCoordinates(flatCoordinates, stride) { + const extent = this.getBufferedMaxExtent(); + const tmpCoord = this.tmpCoordinate_; + const coordinates = this.coordinates; + let myEnd = coordinates.length; + for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + tmpCoord[0] = flatCoordinates[i]; + tmpCoord[1] = flatCoordinates[i + 1]; + if (containsCoordinate(extent, tmpCoord)) { + coordinates[myEnd++] = tmpCoord[0]; + coordinates[myEnd++] = tmpCoord[1]; + } + } + return myEnd; + } + /** * @param {Array} flatCoordinates Flat coordinates. * @param {number} offset Offset. @@ -149,7 +176,7 @@ class CanvasBuilder extends VectorContext { * @protected * @return {number} My end. */ - appendFlatCoordinates( + appendFlatLineCoordinates( flatCoordinates, offset, end, @@ -157,7 +184,8 @@ class CanvasBuilder extends VectorContext { closed, skipFirst ) { - let myEnd = this.coordinates.length; + const coordinates = this.coordinates; + let myEnd = coordinates.length; const extent = this.getBufferedMaxExtent(); if (skipFirst) { offset += stride; @@ -174,15 +202,15 @@ class CanvasBuilder extends VectorContext { nextRel = coordinateRelationship(extent, nextCoord); if (nextRel !== lastRel) { if (skipped) { - this.coordinates[myEnd++] = lastXCoord; - this.coordinates[myEnd++] = lastYCoord; + coordinates[myEnd++] = lastXCoord; + coordinates[myEnd++] = lastYCoord; + skipped = false; } - this.coordinates[myEnd++] = nextCoord[0]; - this.coordinates[myEnd++] = nextCoord[1]; - skipped = false; + coordinates[myEnd++] = nextCoord[0]; + coordinates[myEnd++] = nextCoord[1]; } else if (nextRel === Relationship.INTERSECTING) { - this.coordinates[myEnd++] = nextCoord[0]; - this.coordinates[myEnd++] = nextCoord[1]; + coordinates[myEnd++] = nextCoord[0]; + coordinates[myEnd++] = nextCoord[1]; skipped = false; } else { skipped = true; @@ -194,8 +222,8 @@ class CanvasBuilder extends VectorContext { // Last coordinate equals first or only one point to append: if ((closed && skipped) || i === offset + stride) { - this.coordinates[myEnd++] = lastXCoord; - this.coordinates[myEnd++] = lastYCoord; + coordinates[myEnd++] = lastXCoord; + coordinates[myEnd++] = lastYCoord; } return myEnd; } @@ -211,7 +239,7 @@ class CanvasBuilder extends VectorContext { drawCustomCoordinates_(flatCoordinates, offset, ends, stride, builderEnds) { for (let i = 0, ii = ends.length; i < ii; ++i) { const end = ends[i]; - const builderEnd = this.appendFlatCoordinates( + const builderEnd = this.appendFlatLineCoordinates( flatCoordinates, offset, end, @@ -285,12 +313,9 @@ class CanvasBuilder extends VectorContext { renderer, inflateCoordinatesArray, ]); - } else if ( - type == GeometryType.LINE_STRING || - type == GeometryType.MULTI_POINT - ) { + } else if (type == GeometryType.LINE_STRING) { flatCoordinates = geometry.getFlatCoordinates(); - builderEnd = this.appendFlatCoordinates( + builderEnd = this.appendFlatLineCoordinates( flatCoordinates, 0, flatCoordinates.length, @@ -306,6 +331,19 @@ class CanvasBuilder extends VectorContext { renderer, inflateCoordinates, ]); + } else if (type == GeometryType.MULTI_POINT) { + flatCoordinates = geometry.getFlatCoordinates(); + builderEnd = this.appendFlatPointCoordinates(flatCoordinates, stride); + if (builderEnd > builderBegin) { + this.instructions.push([ + CanvasInstruction.CUSTOM, + builderBegin, + builderEnd, + geometry, + renderer, + inflateCoordinates, + ]); + } } else if (type == GeometryType.POINT) { flatCoordinates = geometry.getFlatCoordinates(); this.coordinates.push(flatCoordinates[0], flatCoordinates[1]); diff --git a/src/ol/render/canvas/ImageBuilder.js b/src/ol/render/canvas/ImageBuilder.js index 4b920d4ad0..e50f2c88a7 100644 --- a/src/ol/render/canvas/ImageBuilder.js +++ b/src/ol/render/canvas/ImageBuilder.js @@ -99,25 +99,6 @@ class CanvasImageBuilder extends CanvasBuilder { this.width_ = undefined; } - /** - * @param {Array} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - * @return {number} My end. - */ - drawCoordinates_(flatCoordinates, offset, end, stride) { - return this.appendFlatCoordinates( - flatCoordinates, - offset, - end, - stride, - false, - false - ); - } - /** * @param {import("../../geom/Point.js").default|import("../Feature.js").default} pointGeometry Point geometry. * @param {import("../../Feature.js").FeatureLike} feature Feature. @@ -130,12 +111,7 @@ class CanvasImageBuilder extends CanvasBuilder { const flatCoordinates = pointGeometry.getFlatCoordinates(); const stride = pointGeometry.getStride(); const myBegin = this.coordinates.length; - const myEnd = this.drawCoordinates_( - flatCoordinates, - 0, - flatCoordinates.length, - stride - ); + const myEnd = this.appendFlatPointCoordinates(flatCoordinates, stride); this.instructions.push([ CanvasInstruction.DRAW_IMAGE, myBegin, @@ -190,12 +166,7 @@ class CanvasImageBuilder extends CanvasBuilder { const flatCoordinates = multiPointGeometry.getFlatCoordinates(); const stride = multiPointGeometry.getStride(); const myBegin = this.coordinates.length; - const myEnd = this.drawCoordinates_( - flatCoordinates, - 0, - flatCoordinates.length, - stride - ); + const myEnd = this.appendFlatPointCoordinates(flatCoordinates, stride); this.instructions.push([ CanvasInstruction.DRAW_IMAGE, myBegin, diff --git a/src/ol/render/canvas/LineStringBuilder.js b/src/ol/render/canvas/LineStringBuilder.js index 0e27feb3ef..ae8850d894 100644 --- a/src/ol/render/canvas/LineStringBuilder.js +++ b/src/ol/render/canvas/LineStringBuilder.js @@ -28,7 +28,7 @@ class CanvasLineStringBuilder extends CanvasBuilder { */ drawFlatCoordinates_(flatCoordinates, offset, end, stride) { const myBegin = this.coordinates.length; - const myEnd = this.appendFlatCoordinates( + const myEnd = this.appendFlatLineCoordinates( flatCoordinates, offset, end, diff --git a/src/ol/render/canvas/PolygonBuilder.js b/src/ol/render/canvas/PolygonBuilder.js index d53206a131..4b182289c1 100644 --- a/src/ol/render/canvas/PolygonBuilder.js +++ b/src/ol/render/canvas/PolygonBuilder.js @@ -40,7 +40,7 @@ class CanvasPolygonBuilder extends CanvasBuilder { for (let i = 0; i < numEnds; ++i) { const end = ends[i]; const myBegin = this.coordinates.length; - const myEnd = this.appendFlatCoordinates( + const myEnd = this.appendFlatLineCoordinates( flatCoordinates, offset, end, @@ -57,7 +57,7 @@ class CanvasPolygonBuilder extends CanvasBuilder { this.hitDetectionInstructions.push(moveToLineToInstruction); if (stroke) { // Performance optimization: only call closePath() when we have a stroke. - // Otherwise the ring is closed already (see appendFlatCoordinates above). + // Otherwise the ring is closed already (see appendFlatLineCoordinates above). this.instructions.push(closePathInstruction); this.hitDetectionInstructions.push(closePathInstruction); } @@ -108,7 +108,7 @@ class CanvasPolygonBuilder extends CanvasBuilder { const flatCoordinates = circleGeometry.getFlatCoordinates(); const stride = circleGeometry.getStride(); const myBegin = this.coordinates.length; - this.appendFlatCoordinates( + this.appendFlatLineCoordinates( flatCoordinates, 0, flatCoordinates.length, diff --git a/src/ol/render/canvas/TextBuilder.js b/src/ol/render/canvas/TextBuilder.js index 98216ed3d7..a5235a2a61 100644 --- a/src/ol/render/canvas/TextBuilder.js +++ b/src/ol/render/canvas/TextBuilder.js @@ -169,15 +169,20 @@ class CanvasTextBuilder extends CanvasBuilder { return; } - let begin = this.coordinates.length; + const coordinates = this.coordinates; + let begin = coordinates.length; const geometryType = geometry.getType(); let flatCoordinates = null; - let end = 2; let stride = geometry.getStride(); - let i, ii; - if (textState.placement === TextPlacement.LINE) { + if ( + textState.placement === TextPlacement.LINE && + (geometryType == GeometryType.LINE_STRING || + geometryType == GeometryType.MULTI_LINE_STRING || + geometryType == GeometryType.POLYGON || + geometryType == GeometryType.MULTI_POLYGON) + ) { if (!intersects(this.getBufferedMaxExtent(), geometry.getExtent())) { return; } @@ -194,7 +199,7 @@ class CanvasTextBuilder extends CanvasBuilder { } else if (geometryType == GeometryType.MULTI_POLYGON) { const endss = /** @type {import("../../geom/MultiPolygon.js").default} */ (geometry).getEndss(); ends = []; - for (i = 0, ii = endss.length; i < ii; ++i) { + for (let i = 0, ii = endss.length; i < ii; ++i) { ends.push(endss[i][0]); } } @@ -216,10 +221,10 @@ class CanvasTextBuilder extends CanvasBuilder { } else { flatEnd = ends[o]; } - for (i = flatOffset; i < flatEnd; i += stride) { - this.coordinates.push(flatCoordinates[i], flatCoordinates[i + 1]); + for (let i = flatOffset; i < flatEnd; i += stride) { + coordinates.push(flatCoordinates[i], flatCoordinates[i + 1]); } - end = this.coordinates.length; + const end = coordinates.length; flatOffset = ends[o]; const declutterGroup = this.declutterGroups_ ? o === 0 @@ -231,15 +236,11 @@ class CanvasTextBuilder extends CanvasBuilder { } this.endGeometry(feature); } else { - let geometryWidths = null; - if (!textState.overflow) { - geometryWidths = []; - } + const geometryWidths = textState.overflow ? null : []; switch (geometryType) { case GeometryType.POINT: case GeometryType.MULTI_POINT: flatCoordinates = /** @type {import("../../geom/MultiPoint.js").default} */ (geometry).getFlatCoordinates(); - end = flatCoordinates.length; break; case GeometryType.LINE_STRING: flatCoordinates = /** @type {import("../../geom/LineString.js").default} */ (geometry).getFlatMidpoint(); @@ -250,7 +251,6 @@ class CanvasTextBuilder extends CanvasBuilder { case GeometryType.MULTI_LINE_STRING: flatCoordinates = /** @type {import("../../geom/MultiLineString.js").default} */ (geometry).getFlatMidpoints(); stride = 2; - end = flatCoordinates.length; break; case GeometryType.POLYGON: flatCoordinates = /** @type {import("../../geom/Polygon.js").default} */ (geometry).getFlatInteriorPoint(); @@ -262,28 +262,23 @@ class CanvasTextBuilder extends CanvasBuilder { case GeometryType.MULTI_POLYGON: const interiorPoints = /** @type {import("../../geom/MultiPolygon.js").default} */ (geometry).getFlatInteriorPoints(); flatCoordinates = []; - for (i = 0, ii = interiorPoints.length; i < ii; i += 3) { + for (let i = 0, ii = interiorPoints.length; i < ii; i += 3) { if (!textState.overflow) { geometryWidths.push(interiorPoints[i + 2] / this.resolution); } flatCoordinates.push(interiorPoints[i], interiorPoints[i + 1]); } - stride = 2; - end = flatCoordinates.length; - if (end == 0) { + if (flatCoordinates.length === 0) { return; } + stride = 2; break; default: } - end = this.appendFlatCoordinates( - flatCoordinates, - 0, - end, - stride, - false, - false - ); + const end = this.appendFlatPointCoordinates(flatCoordinates, stride); + if (end === begin) { + return; + } this.saveTextStates_(); diff --git a/test/spec/ol/renderer/canvas/builder.test.js b/test/spec/ol/renderer/canvas/builder.test.js index 473aa096a2..9ceda6f06f 100644 --- a/test/spec/ol/renderer/canvas/builder.test.js +++ b/test/spec/ol/renderer/canvas/builder.test.js @@ -331,7 +331,7 @@ describe('ol.render.canvas.Builder', function () { }); }); - describe('#appendFlatCoordinates()', function () { + describe('#appendFlatLineCoordinates()', function () { let replay; beforeEach(function () { replay = new CanvasBuilder(1, [-180, -90, 180, 90], 1, 1, true); @@ -339,19 +339,19 @@ describe('ol.render.canvas.Builder', function () { it('appends coordinates that are within the max extent', function () { const flat = [-110, 45, 110, 45, 110, -45, -110, -45]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql(flat); }); it('appends polygon coordinates that are within the max extent', function () { const flat = [-110, 45, 110, 45, 110, -45, -110, -45, -110, 45]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, false); expect(replay.coordinates).to.eql(flat); }); it('appends polygon coordinates that are within the max extent (skipping first)', function () { const flat = [-110, 45, 110, 45, 110, -45, -110, -45, -110, 45]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, true); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, true); expect(replay.coordinates).to.eql([ 110, 45, @@ -366,7 +366,7 @@ describe('ol.render.canvas.Builder', function () { it('works with a single coordinate (inside)', function () { const flat = [-110, 45]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql(flat); }); @@ -374,7 +374,7 @@ describe('ol.render.canvas.Builder', function () { // this could be changed, but to make the code simpler for properly // closing rings, we always add the first point const flat = [-110, 145]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql(flat); }); @@ -382,13 +382,13 @@ describe('ol.render.canvas.Builder', function () { // this could be changed, but to make the code simpler for properly // closing rings, we always add the first point const flat = [-110, 145, -110, 145]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, false); expect(replay.coordinates).to.eql(flat); }); it('skips first polygon vertex upon request (also when outside)', function () { const flat = [-110, 145, -110, 145]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, true); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, true); expect(replay.coordinates).to.eql([-110, 145]); }); @@ -396,13 +396,13 @@ describe('ol.render.canvas.Builder', function () { // this means we get a few extra points when coordinates are not // part of a linestring or ring, but only a few extra const flat = [0, 200, 0, -200]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql(flat); }); it('appends points when segments cross (top to inside)', function () { const flat = [0, 200, 0, 0]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql(flat); }); @@ -410,7 +410,7 @@ describe('ol.render.canvas.Builder', function () { // this could be changed, but to make the code simpler for properly // closing rings, we always add the first segment const flat = [-10, 200, 10, 200]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql(flat); }); @@ -418,67 +418,67 @@ describe('ol.render.canvas.Builder', function () { // this could be changed, but to make the code simpler for properly // closing rings, we always add the first segment const flat = [-10, 200, 10, 200, -10, 200]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, false); expect(replay.coordinates).to.eql(flat); }); it('skips first polygon segment upon request (also when outside)', function () { const flat = [-10, 200, 10, 200, -10, 200]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, true); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, true); expect(replay.coordinates).to.eql([10, 200, -10, 200]); }); it('eliminates segments outside (and not changing rel)', function () { const flat = [0, 0, 0, 200, 5, 200, 10, 200]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql([0, 0, 0, 200]); }); it('eliminates polygon segments outside (and not changing rel)', function () { const flat = [0, 0, 0, 200, 5, 200, 10, 200, 0, 0]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, false); expect(replay.coordinates).to.eql([0, 0, 0, 200, 10, 200, 0, 0]); }); it('eliminates polygon segments outside (skipping first and not changing rel)', function () { const flat = [0, 0, 0, 10, 0, 200, 5, 200, 10, 200, 0, 0]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, true); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, true); expect(replay.coordinates).to.eql([0, 10, 0, 200, 10, 200, 0, 0]); }); it('eliminates segments outside (and not changing rel)', function () { const flat = [0, 0, 0, 200, 10, 200]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql([0, 0, 0, 200]); }); it('includes polygon segments outside (and not changing rel) when on last segment', function () { const flat = [0, 0, 0, 200, 10, 200, 0, 0]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, false); expect(replay.coordinates).to.eql(flat); }); it('includes polygon segments outside (skipping first and not changing rel) when on last segment', function () { const flat = [0, 0, 0, 200, 10, 200, 0, 0]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, true); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, true); expect(replay.coordinates).to.eql([0, 200, 10, 200, 0, 0]); }); it('includes outside segments that change relationship', function () { const flat = [0, 0, 0, 200, 200, 200, 250, 200]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, false, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, false, false); expect(replay.coordinates).to.eql([0, 0, 0, 200, 200, 200]); }); it('includes outside polygon segments that change relationship when on last segment', function () { const flat = [0, 0, 0, 200, 200, 200, 250, 200, 0, 0]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, false); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, false); expect(replay.coordinates).to.eql(flat); }); it('includes outside polygon segments that change relationship when on last segment (when skipping first)', function () { const flat = [0, 0, 0, 200, 200, 200, 250, 200, 0, 0]; - replay.appendFlatCoordinates(flat, 0, flat.length, 2, true, true); + replay.appendFlatLineCoordinates(flat, 0, flat.length, 2, true, true); expect(replay.coordinates).to.eql([0, 200, 200, 200, 250, 200, 0, 0]); }); });