Skip to content

Commit 18deb75

Browse files
authored
feat: allow to modifiy circle geometry (#270)
* chore(release): 2.2.1-beta.0 * fix: allow circle geom for snapping * chore(release): 2.2.1-beta.1 * fix: allow circle geom for snapping * chore(release): 2.2.1-beta.2 * fix: manage circle geometry for snaplines * chore(release): 2.2.1-beta.3 * build: update dependencies * fix: clean cad getClosestFeature behavior * chore(release): 2.2.1-beta.4 * chore: clean code * chore: keep default behavior * chore: update dependencies * chore: remove uselles eslint rules * chore: use default value
1 parent ca99c50 commit 18deb75

File tree

12 files changed

+1696
-1109
lines changed

12 files changed

+1696
-1109
lines changed

.eslintrc.json

+1-4
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111
},
1212
"plugins": ["cypress", "prettier"],
1313
"rules": {
14-
"prettier/prettier": "error",
15-
"arrow-body-style": "off",
16-
"import/no-named-as-default": "off",
17-
"import/no-named-as-default-member": "off"
14+
"prettier/prettier": "error"
1815
}
1916
}

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
20

cypress/e2e/control/cad.spec.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
const FORCE = { force: true };
22

33
const coordToFixed = (coordArray, decimals) => {
4-
return [
4+
const arr = [
55
parseFloat(coordArray[0].toFixed(decimals)),
66
parseFloat(coordArray[1].toFixed(decimals)),
77
];
8+
return arr;
89
};
910

1011
describe('CAD control', () => {

package.json

+22-21
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,31 @@
1010
"ol": "^7"
1111
},
1212
"devDependencies": {
13-
"@commitlint/cli": "17.6.1",
14-
"@commitlint/config-conventional": "17.6.1",
15-
"cypress": "12.10.0",
16-
"esbuild": "0.17.17",
17-
"eslint": "8.38.0",
13+
"@commitlint/cli": "19.3.0",
14+
"@commitlint/config-conventional": "19.2.2",
15+
"cypress": "13.9.0",
16+
"esbuild": "0.21.1",
17+
"eslint": "8",
1818
"eslint-config-airbnb-base": "15.0.0",
19-
"eslint-config-prettier": "8.8.0",
20-
"eslint-plugin-cypress": "2.13.2",
21-
"eslint-plugin-import": "2.27.5",
22-
"eslint-plugin-prettier": "4.2.1",
19+
"eslint-config-prettier": "9.1.0",
20+
"eslint-plugin-cypress": "3.2.0",
21+
"eslint-plugin-import": "2.29.1",
22+
"eslint-plugin-prettier": "5.1.3",
2323
"fixpack": "4.0.0",
24-
"husky": "8.0.3",
24+
"husky": "9.0.11",
2525
"is-ci": "3.0.1",
26-
"jsdoc": "4.0.2",
26+
"jsdoc": "4.0.3",
2727
"jsdoc-export-default-interop": "0.3.1",
28-
"jsts": "2.9.3",
29-
"lint-staged": "13.2.1",
30-
"ol": "7.3.0",
31-
"prettier": "2.8.7",
28+
"jsts": "2.11.3",
29+
"lint-staged": "15.2.2",
30+
"ol": "8",
31+
"prettier": "3.2.5",
3232
"shx": "0.3.4",
3333
"standard-version": "9.5.0",
34-
"start-server-and-test": "2.0.0",
35-
"stylelint": "15.5.0",
36-
"stylelint-config-standard": "33.0.0",
37-
"typescript": "5.0.4"
34+
"start-server-and-test": "2.0.3",
35+
"stylelint": "16.5.0",
36+
"stylelint-config-standard": "36.0.0",
37+
"typescript": "5.4.5"
3838
},
3939
"scripts": {
4040
"build": "shx rm -rf build && tsc --project config/tsconfig-build.json && esbuild build/index.js --bundle --global-name=ole --loader:.svg=dataurl --minify --outfile=build/bundle.js",
@@ -43,14 +43,15 @@
4343
"cy:test": "start-server-and-test start http://127.0.0.1:8000 cy:run",
4444
"doc": "jsdoc -p -r -c jsdoc_conf.json src -d doc README.md && shx cp build/bundle.js index.js",
4545
"format": "prettier --write 'cypress/integration/*.js' 'src/**/*.js' && eslint 'src/**/*.js' --fix && stylelint 'style/**/*.css' 'src/**/*.css' 'src/**/*.scss' --fix",
46-
"lint": "eslint 'cypress/e2e/**/*.js' 'src/**/*.js' && stylelint 'style/**/*.css' 'src/**/*.css' 'src/**/*.scss'",
46+
"lint": "ESLINT_USE_FLAT_CONFIG=false eslint 'cypress/e2e/**/*.js' 'src/**/*.js' && stylelint 'style/**/*.css' 'src/**/*.css' 'src/**/*.scss'",
4747
"prepare": "is-ci || husky install",
4848
"publish:beta": "yarn release -- --prerelease beta --skip.changelog && yarn build && git push origin HEAD && git push --tags && yarn publish --tag beta",
4949
"publish:beta:dryrun": "yarn release -- --prerelease beta --dry-run --skip.changelog",
5050
"publish:public": "yarn release && yarn build && git push origin HEAD && git push --tags && yarn publish",
5151
"publish:public:dryrun": "yarn release --dry-run",
5252
"release": "standard-version",
53-
"start": "esbuild src/index.js --bundle --global-name=ole --loader:.svg=dataurl --minify --outfile=index.js --serve=localhost:8000 --servedir=. --sourcemap --watch=forever"
53+
"start": "esbuild src/index.js --bundle --global-name=ole --loader:.svg=dataurl --minify --outfile=index.js --serve=localhost:8000 --servedir=. --sourcemap --watch=forever",
54+
"up": "yarn upgrade-interactive --latest"
5455
},
5556
"keywords": [
5657
"Editor",

src/control/cad.js

+39-46
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { Style, Stroke } from 'ol/style';
2-
import { Point, LineString, Polygon, MultiPoint } from 'ol/geom';
2+
import { Point, LineString, Polygon, MultiPoint, Circle } from 'ol/geom';
33
import Feature from 'ol/Feature';
44
import Vector from 'ol/layer/Vector';
55
import VectorSource from 'ol/source/Vector';
66
import { Pointer, Snap } from 'ol/interaction';
77
import { OverlayOp } from 'jsts/org/locationtech/jts/operation/overlay';
8+
import { getUid } from 'ol/util';
89
import Control from './control';
910
import cadSVG from '../../img/cad.svg';
1011
import { SnapEvent, SnapEventType } from '../event';
@@ -143,7 +144,7 @@ class CadControl extends Control {
143144
* @type {Function}
144145
* @private
145146
*/
146-
this.filter = options.filter || null;
147+
this.filter = options.filter || (() => true);
147148

148149
/**
149150
* Interaction for snapping
@@ -246,47 +247,33 @@ class CadControl extends Control {
246247
* to a given coordinate.
247248
* @private
248249
* @param {ol.Coordinate} coordinate Coordinate.
249-
* @param {Number} numFeatures Number of features to search.
250+
* @param {Number} nbFeatures Number of features to search.
250251
* @returns {Array.<ol.Feature>} List of closest features.
251252
*/
252-
getClosestFeatures(coordinate, numFeatures) {
253-
const num = numFeatures || 1;
254-
const ext = [-Infinity, -Infinity, Infinity, Infinity];
255-
const featureDict = {};
256-
257-
const pushSnapFeatures = (f) => {
258-
const cCoord = f.getGeometry().getClosestPoint(coordinate);
259-
const dx = cCoord[0] - coordinate[0];
260-
const dy = cCoord[1] - coordinate[1];
261-
const dist = dx * dx + dy * dy;
262-
featureDict[dist] = f;
263-
};
264-
265-
this.source.forEachFeatureInExtent(ext, (f) => {
266-
if (!this.filter || (this.filter && this.filter(f))) {
267-
pushSnapFeatures(f);
268-
}
269-
});
270-
271-
const dists = Object.keys(featureDict);
272-
let features = [];
273-
const count = Math.min(dists.length, num);
274-
275-
dists.sort((a, b) => a - b);
276-
277-
for (let i = 0; i < count; i += 1) {
278-
features.push(featureDict[dists[i]]);
279-
}
280-
281-
// Remove edit and draw feature for snapping list.
253+
getClosestFeatures(coordinate, nbFeatures = 1) {
282254
const editFeature = this.editor.getEditFeature();
283255
const drawFeature = this.editor.getDrawFeature();
284-
[editFeature, drawFeature].forEach((feature) => {
285-
const index = features.indexOf(feature);
286-
if (index > -1) {
287-
features.splice(index, 1);
256+
const currentFeatures = [editFeature, drawFeature].filter((f) => !!f);
257+
258+
const cacheDist = {};
259+
const dist = (f) => {
260+
const uid = getUid(f);
261+
if (!cacheDist[uid]) {
262+
const cCoord = f.getGeometry().getClosestPoint(coordinate);
263+
const dx = cCoord[0] - coordinate[0];
264+
const dy = cCoord[1] - coordinate[1];
265+
cacheDist[uid] = dx * dx + dy * dy;
288266
}
289-
});
267+
return cacheDist[uid];
268+
};
269+
const sortByDistance = (a, b) => dist(a) - dist(b);
270+
271+
let features = this.source
272+
.getFeatures()
273+
.filter(this.filter)
274+
.filter((f) => !currentFeatures.includes(f))
275+
.sort(sortByDistance)
276+
.slice(0, nbFeatures);
290277

291278
// When using showSnapPoints, return all features except edit/draw features
292279
if (this.properties.showSnapPoints) {
@@ -295,10 +282,10 @@ class CadControl extends Control {
295282

296283
// When using showSnapLines, return all features but edit/draw features are
297284
// cloned to remove the node at the mouse position.
298-
[editFeature, drawFeature]
299-
.filter((f) => f)
300-
.forEach((feature) => {
301-
const geom = feature.getGeometry();
285+
currentFeatures.filter(this.filter).forEach((feature) => {
286+
const geom = feature.getGeometry();
287+
288+
if (!(geom instanceof Circle) && !(geom instanceof Point)) {
302289
const snapGeom = getShiftedMultiPoint(geom, coordinate);
303290
const isPolygon = geom instanceof Polygon;
304291
const snapFeature = feature.clone();
@@ -308,7 +295,8 @@ class CadControl extends Control {
308295
isPolygon ? [snapGeom.getCoordinates()] : snapGeom.getCoordinates(),
309296
);
310297
features = [snapFeature, ...features];
311-
});
298+
}
299+
});
312300

313301
return features;
314302
}
@@ -615,10 +603,15 @@ class CadControl extends Control {
615603

616604
for (let i = 0; i < features.length; i += 1) {
617605
const geom = features[i].getGeometry();
618-
const featureCoord = geom.getCoordinates();
606+
let featureCoord = geom.getCoordinates();
607+
608+
if (!featureCoord && geom instanceof Circle) {
609+
featureCoord = geom.getCenter();
610+
}
611+
619612
// Polygons initially return a geometry with an empty coordinate array, so we need to catch it
620-
if (featureCoord.length) {
621-
if (geom instanceof Point) {
613+
if (featureCoord?.length) {
614+
if (geom instanceof Point || geom instanceof Circle) {
622615
snapCoordsBefore.push();
623616
snapCoords.push(featureCoord);
624617
snapCoordsAfter.push();

src/control/draw.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class DrawControl extends Control {
5656
});
5757

5858
this.drawInteraction.on('drawend', () => {
59-
this.editor.setDrawFeature(null);
59+
this.editor.setDrawFeature();
6060
});
6161
}
6262

src/control/modify.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class ModifyControl extends Control {
170170
});
171171

172172
this.moveInteraction.on('moveend', () => {
173-
this.editor.setEditFeature(null);
173+
this.editor.setEditFeature();
174174
this.isMoving = false;
175175
});
176176
this.moveInteraction.setActive(false);
@@ -198,7 +198,7 @@ class ModifyControl extends Control {
198198
});
199199

200200
this.modifyInteraction.on('modifyend', () => {
201-
this.editor.setEditFeature(null);
201+
this.editor.setEditFeature();
202202
this.isModifying = false;
203203
});
204204
this.modifyInteraction.setActive(false);

src/helper/getEquationOfLine.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ const getEquationOfLine = (coordA, coordB) => {
1010
}
1111
const m = (yB - yA) / (xB - xA);
1212
const b = yB - m * xB;
13-
return (x) => {
14-
return m * x + b;
15-
};
13+
return (x) => m * x + b;
1614
};
1715

1816
export default getEquationOfLine;

src/helper/getProjectedPoint.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
const dotProduct = (e1, e2) => {
2-
return e1[0] * e2[0] + e1[1] * e2[1];
3-
};
1+
const dotProduct = (e1, e2) => e1[0] * e2[0] + e1[1] * e2[1];
42

53
/**
64
* Get projected point P' of P on line e1. Faster version.

src/helper/getShiftedMultiPoint.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ const getShiftedMultipoint = (geometry, coordinate) => {
1919

2020
// Exclude the node being modified
2121
shiftedMultipoint.setCoordinates(
22-
shiftedMultipoint.getCoordinates().filter((coord) => {
23-
return coord.toString() !== drawNodeCoordinate.toString();
24-
}),
22+
shiftedMultipoint
23+
.getCoordinates()
24+
.filter((coord) => coord.toString() !== drawNodeCoordinate.toString()),
2525
);
2626

2727
return shiftedMultipoint;

src/interaction/delete.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ class Delete extends Interaction {
1414
this.condition =
1515
options.condition ||
1616
((mapBrowserEvent) => {
17-
return (
17+
const bool =
1818
noModifierKeys(mapBrowserEvent) &&
1919
targetNotEditable(mapBrowserEvent) &&
2020
(mapBrowserEvent.originalEvent.keyCode === 46 ||
21-
mapBrowserEvent.originalEvent.keyCode === 8)
22-
);
21+
mapBrowserEvent.originalEvent.keyCode === 8);
22+
return bool;
2323
});
2424
}
2525

0 commit comments

Comments
 (0)