From feb03ce1c5fa377672d9bdd2c1408abec0eacf79 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Wed, 22 May 2024 01:36:06 -0400 Subject: [PATCH] feat: limit max guides generated per feature via an option --- package.json | 1 - src/control/cad.js | 75 +++++++++++++++++++++++++++++++++------------- yarn.lock | 2 +- 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 9d9d282b..aec4dac9 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "description": "OpenLayers Editor", "version": "2.4.4", "main": "build/index.js", - "dependencies": {}, "peerDependencies": { "jsts": ">=2", "lodash.throttle": ">=4", diff --git a/src/control/cad.js b/src/control/cad.js index 0f783a7b..8c64cec3 100644 --- a/src/control/cad.js +++ b/src/control/cad.js @@ -1,9 +1,11 @@ +import { times, sortedIndexBy } from 'lodash'; import { Style, Stroke } from 'ol/style'; import { Point, LineString, Polygon, MultiPoint, Circle } from 'ol/geom'; import Feature from 'ol/Feature'; import Vector from 'ol/layer/Vector'; import VectorSource from 'ol/source/Vector'; import { Pointer, Snap } from 'ol/interaction'; +import { distance as euclideanDistance } from 'ol/coordinate'; import { OverlayOp } from 'jsts/org/locationtech/jts/operation/overlay'; import { getUid } from 'ol/util'; import Control from './control'; @@ -40,6 +42,8 @@ class CadControl extends Control { * @param {Function} [options.lineFilter] An optional filter for the generated snapping lines * array (takes the lines and cursor coordinate as arguments and returns the new line array) * @param {Number} [options.nbClosestFeatures] Number of features to use for snapping (closest first). Default is 5. + * @param {Number} [options.limitGuidesPerFeature] A feature with complex geometry may generate + * a large number of lines. This option can be used to limit the guides to the n closest segments. Deafult is 3. * @param {Number} [options.snapTolerance] Snap tolerance in pixel * for snap lines. Default is 10. * @param {Boolean} [options.showSnapLines] Whether to show @@ -134,6 +138,18 @@ class CadControl extends Control { this.nbClosestFeatures = options.nbClosestFeatures === undefined ? 5 : options.nbClosestFeatures; + /** + * Number of guide lines to limit cad to generate for a single feature. + * this.nbClosestFeatures * this.limitGuidesPerFeature will be the maximum + * number of guides shown at any time. Default is 3. + * @type {Number} + * @private + */ + this.limitGuidesPerFeature = + options.limitGuidesPerFeature === undefined + ? 3 + : options.limitGuidesPerFeature; + /** * Snap tolerance in pixel. * @type {Number} @@ -505,7 +521,6 @@ class CadControl extends Control { getSegmentLines(coordinate, snapCoords, snapCoordsBefore) { const mousePx = this.map.getPixelFromCoordinate(coordinate); const doubleTol = this.snapTolerance * 2; - const [mouseX, mouseY] = mousePx; const lines = []; for (let i = 0; i < snapCoords.length; i += 1) { @@ -522,13 +537,12 @@ class CadControl extends Control { // Calculate projected point const projMousePx = getProjectedPoint(mousePx, snapPxBefore, snapPx); - const [projMouseX, projMouseY] = projMousePx; - const distance = Math.sqrt( - (projMouseX - mouseX) ** 2 + (projMouseY - mouseY) ** 2, - ); + const distance = euclideanDistance(mousePx, projMousePx); + let newPt; if (distance <= this.snapTolerance) { + const [projMouseX, projMouseY] = projMousePx; // lineFunc is undefined when it's a vertical line const lineFunc = getEquationOfLine(snapPxBefore, snapPx); const newX = projMouseX + (projMouseX < snapX ? -doubleTol : doubleTol); @@ -557,7 +571,6 @@ class CadControl extends Control { getOrthoLines(coordinate, snapCoords, snapCoordsBefore) { const mousePx = this.map.getPixelFromCoordinate(coordinate); const doubleTol = this.snapTolerance * 2; - const [mouseX, mouseY] = mousePx; const lines = []; for (let i = 0; i < snapCoords.length; i += 1) { @@ -579,13 +592,11 @@ class CadControl extends Control { [orthoLine1, orthoLine2].forEach((line) => { const [anchorPx, last] = line.getCoordinates(); const projMousePx = getProjectedPoint(mousePx, anchorPx, last); - const [projMouseX, projMouseY] = projMousePx; - const distance = Math.sqrt( - (projMouseX - mouseX) ** 2 + (projMouseY - mouseY) ** 2, - ); + const distance = euclideanDistance(mousePx, projMousePx); let newPt; if (distance <= this.snapTolerance) { + const [projMouseX, projMouseY] = projMousePx; // lineFunc is undefined when it's a vertical line const lineFunc = getEquationOfLine(anchorPx, projMousePx); const newX = @@ -623,6 +634,38 @@ class CadControl extends Control { const snapCoords = []; const snapCoordsAfter = []; // store the direct next point in the coordinate array + // Parse the list of coords, we will add the ortho and segment lines which + // are closest to the users cursor up until the limit of `this.limitGuidesPerFeature` + const parseCoordList = (coords) => { + let includedCoords; + if (coords.length > this.limitGuidesPerFeature) { + includedCoords = []; + // Check the euclidean distance between coord at index and the + // included vertices so far. Create an array of `this.limitGuidesPerFeature` + // closest items. + for (let index = 0; index < coords.length; index += 1) { + const addIndex = sortedIndexBy(includedCoords, index, (i) => + euclideanDistance(coords[i], coordinate), + ); + if (addIndex < this.limitGuidesPerFeature) { + includedCoords.splice( + addIndex, + includedCoords.length === this.limitGuidesPerFeature ? 1 : 0, + index, + ); + } + } + } else { + includedCoords = times(coords.length); + } + + includedCoords.forEach((index) => { + snapCoordsBefore.push(coords[index - 1]); + snapCoords.push(coords[index]); + snapCoordsAfter.push(coords[index + 1]); + }); + }; + for (let i = 0; i < features.length; i += 1) { const geom = features[i].getGeometry(); let featureCoord = geom.getCoordinates(); @@ -641,17 +684,9 @@ class CadControl extends Control { // Add feature vertices // eslint-disable-next-line no-lonely-if if (geom instanceof LineString) { - for (let j = 0; j < featureCoord.length; j += 1) { - snapCoordsBefore.push(featureCoord[j - 1]); - snapCoords.push(featureCoord[j]); - snapCoordsAfter.push(featureCoord[j + 1]); - } + parseCoordList(featureCoord); } else if (geom instanceof Polygon) { - for (let j = 0; j < featureCoord[0].length; j += 1) { - snapCoordsBefore.push(featureCoord[0][j - 1]); - snapCoords.push(featureCoord[0][j]); - snapCoordsAfter.push(featureCoord[0][j + 1]); - } + parseCoordList(featureCoord[0]); } // Add extent vertices diff --git a/yarn.lock b/yarn.lock index ae37cc5b..f294c27b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3904,7 +3904,7 @@ lodash.upperfirst@^4.3.1: lodash@^4.0.1, lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://npm.clearpathrobotics.com:443/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.0.0: