Skip to content

Commit fc320b4

Browse files
authored
Geometry Edit control point support collision (#2213)
* Geometry Edit control point support collision * updates * spec * updates
1 parent 5e7073e commit fc320b4

File tree

4 files changed

+107
-27
lines changed

4 files changed

+107
-27
lines changed

src/geometry/editor/GeometryEditor.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ const options = {
3434
//symbols of edit handles
3535
'centerHandleSymbol': createHandleSymbol('ellipse', 1),
3636
'vertexHandleSymbol': createHandleSymbol('square', 1),
37-
'newVertexHandleSymbol': createHandleSymbol('square', 0.4)
37+
'newVertexHandleSymbol': createHandleSymbol('square', 0.4),
38+
'collision': false,
39+
'collisionBufferSize': 0
3840
};
3941

4042
/**

src/renderer/edit/EditHandle.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { isNil } from '../../core/util/';
77
import { getSymbolHash } from '../../core/util/style';
88
import { getEventContainerPoint } from '../../core/util/dom';
99
import DragHandler from '../../handler/Drag';
10+
import { bufferBBOX, getDefaultBBOX } from '../../core/util/bbox';
1011

1112
const resources = new ResourceCache();
1213
let prevX, prevY;
@@ -92,7 +93,7 @@ export default class EditHandle extends Eventable(Class) {
9293
if (x + w > 0 && x < map.width && y + h > 0 && y < map.height) {
9394
const dpr = map.getDevicePixelRatio();
9495
ctx.globalAlpha = this.opacity;
95-
ctx.drawImage(this._img, Math.round((x + dx) * dpr), Math.round((y + dy) * dpr), Math.round(w * dpr), Math.round(h * dpr));
96+
ctx.drawImage(this._img, Math.round((x + dx) * dpr), Math.round((y + dy) * dpr), Math.round(w * dpr), Math.round(h * dpr));
9697
return true;
9798
}
9899
return false;
@@ -199,4 +200,37 @@ export default class EditHandle extends Eventable(Class) {
199200
containerPoint
200201
});
201202
}
203+
204+
needCollision() {
205+
const { target } = this;
206+
return target && target.options && target.options.collision;
207+
}
208+
209+
getRenderBBOX(dpr) {
210+
const { target, map } = this;
211+
if (!target || !target.options || !map) {
212+
return null;
213+
}
214+
const symbol = this.options['symbol'];
215+
const dx = symbol['markerDx'] || 0;
216+
const dy = symbol['markerDy'] || 0;
217+
const { x, y } = this._point;
218+
const w = this.w;
219+
const h = this.h;
220+
dpr = dpr || map.getDevicePixelRatio();
221+
this.bbox = this.bbox || getDefaultBBOX();
222+
const x1 = Math.round((x + dx) * dpr);
223+
const y1 = Math.round((y + dy) * dpr);
224+
const width = Math.round(w * dpr);
225+
const height = Math.round(h * dpr);
226+
this.bbox[0] = x1;
227+
this.bbox[1] = y1;
228+
this.bbox[2] = x1 + width;
229+
this.bbox[3] = y1 + height;
230+
231+
const { options } = target;
232+
const collisionBufferSize = options.collisionBufferSize || 0;
233+
bufferBBOX(this.bbox, collisionBufferSize);
234+
return this.bbox;
235+
}
202236
}

src/renderer/map/MapCanvasRenderer.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import Point from '../../geo/Point';
66
import Canvas2D from '../../core/Canvas';
77
import MapRenderer from './MapRenderer';
88
import Map from '../../map/Map';
9+
import CollisionIndex from '../../core/CollisionIndex';
10+
11+
const tempCollisionIndex = new CollisionIndex();
912

1013
/**
1114
* @classdesc
@@ -1001,12 +1004,32 @@ class MapCanvasRenderer extends MapRenderer {
10011004
drawTops() {
10021005
// clear topLayer
10031006
this.topCtx.clearRect(0, 0, this.topLayer.width, this.topLayer.height);
1007+
const collisionIndex = tempCollisionIndex;
1008+
collisionIndex.clear();
10041009
this.map.fire('drawtopstart');
10051010
this.map.fire('drawtops');
10061011
const tops = this.getTopElements();
10071012
let updated = false;
1013+
const dpr = this.map.getDevicePixelRatio();
1014+
const geos = [];
10081015
for (let i = 0; i < tops.length; i++) {
1009-
if (tops[i].render(this.topCtx)) {
1016+
const top = tops[i];
1017+
if (top.needCollision && top.needCollision()) {
1018+
const bbox = top.getRenderBBOX(dpr);
1019+
if (bbox) {
1020+
if (collisionIndex.collides(bbox)) {
1021+
const geometry = top.target && top.target._geometry;
1022+
if (geometry && geos.indexOf(geometry) === -1) {
1023+
geos.push(geometry);
1024+
geometry.fire('handlecollision');
1025+
}
1026+
continue;
1027+
} else {
1028+
collisionIndex.insertBox(bbox);
1029+
}
1030+
}
1031+
}
1032+
if (top.render(this.topCtx)) {
10101033
updated = true;
10111034
}
10121035
}

test/geometry/edit/GeometryEditSpec.js

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ describe('Geometry.Edit', function () {
1010
point._add(offset);
1111
}
1212
happen.mousedown(eventContainer, {
13-
'clientX':point.x,
14-
'clientY':point.y
13+
'clientX': point.x,
14+
'clientY': point.y
1515
});
1616
for (var i = 0; i < 10; i++) {
1717
happen.mousemove(document, {
18-
'clientX':point.x + i,
19-
'clientY':point.y + i
18+
'clientX': point.x + i,
19+
'clientY': point.y + i
2020
});
2121
}
2222
happen.mouseup(document);
@@ -107,7 +107,7 @@ describe('Geometry.Edit', function () {
107107
it('not all markers can be edited', function () {
108108
for (var i = 0; i < COMMON_SYMBOL_TESTOR.markerSymbols.length; i++) {
109109
var symbol = COMMON_SYMBOL_TESTOR.markerSymbols[i];
110-
var marker = new maptalks.Marker(center, { symbol:symbol });
110+
var marker = new maptalks.Marker(center, { symbol: symbol });
111111
marker.addTo(layer);
112112
marker.startEdit();
113113
if (symbol['text-name']) {
@@ -129,10 +129,10 @@ describe('Geometry.Edit', function () {
129129

130130
it('resize a vector marker', function () {
131131
var marker = new maptalks.Marker(map.getCenter(), {
132-
symbol : {
133-
markerType:'ellipse',
134-
markerWidth:20,
135-
markerHeight:20
132+
symbol: {
133+
markerType: 'ellipse',
134+
markerWidth: 20,
135+
markerHeight: 20
136136
}
137137
}).addTo(layer);
138138
var size = marker.getSize();
@@ -159,17 +159,17 @@ describe('Geometry.Edit', function () {
159159

160160
it('resize a vector marker with fix aspect ratio', function () {
161161
var marker = new maptalks.Marker(map.getCenter(), {
162-
symbol : {
163-
markerType:'ellipse',
164-
markerWidth:20,
165-
markerHeight:20
162+
symbol: {
163+
markerType: 'ellipse',
164+
markerWidth: 20,
165+
markerHeight: 20
166166
}
167167
}).addTo(layer);
168168
var fired = false;
169169
marker.on('resizing', function () {
170170
fired = true;
171171
});
172-
marker.startEdit({ 'fixAspectRatio' : true });
172+
marker.startEdit({ 'fixAspectRatio': true });
173173
var size = marker.getSize();
174174
dragGeometry(marker, new maptalks.Point(size.width / 2, 0));
175175
var symbol = marker.getSymbol();
@@ -259,7 +259,7 @@ describe('Geometry.Edit', function () {
259259
ellipse.on('resizing', function () {
260260
fired = true;
261261
});
262-
ellipse.startEdit({ 'fixAspectRatio' : true });
262+
ellipse.startEdit({ 'fixAspectRatio': true });
263263
var size = ellipse.getSize();
264264
var ratio = ellipse.getWidth() / ellipse.getHeight();
265265
dragGeometry(ellipse, new maptalks.Point(size.width / 2, 0));
@@ -307,7 +307,7 @@ describe('Geometry.Edit', function () {
307307
rect.on('resizing', function () {
308308
fired = true;
309309
});
310-
rect.startEdit({ 'fixAspectRatio' : true });
310+
rect.startEdit({ 'fixAspectRatio': true });
311311
var size = rect.getSize();
312312
var ratio = rect.getWidth() / rect.getHeight();
313313
dragGeometry(rect, new maptalks.Point(size.width / 2, 0));
@@ -334,7 +334,7 @@ describe('Geometry.Edit', function () {
334334
var size = polygon.getSize();
335335
dragGeometry(polygon, new maptalks.Point(size.width / 2, size.height / 2));
336336
expect(polygon.toGeoJSON()).not.to.be.eqlGeoJSON(o);
337-
var expected = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[118.84682499999997,32.04653400000004],[118.85742312186676,32.04653400000004],[118.85751916135894,32.04196057399085],[118.84682499999997,32.04204242358057],[118.84682499999997,32.04653400000004]]]},"properties":null};
337+
var expected = { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[118.84682499999997, 32.04653400000004], [118.85742312186676, 32.04653400000004], [118.85751916135894, 32.04196057399085], [118.84682499999997, 32.04204242358057], [118.84682499999997, 32.04653400000004]]] }, "properties": null };
338338
expect(polygon.toGeoJSON()).to.be.eqlGeoJSON(expected);
339339

340340
polygon.undoEdit();
@@ -351,17 +351,17 @@ describe('Geometry.Edit', function () {
351351
expect(polygon.getCoordinates()[0].length).to.be(5);
352352

353353
polygon.startEdit({
354-
'removeVertexOn' : 'contextmenu'
354+
'removeVertexOn': 'contextmenu'
355355
});
356356
var size = polygon.getSize();
357357
var domPosition = GET_PAGE_POSITION(container);
358358
var point = map.coordinateToContainerPoint(polygon.getCenter()).add(domPosition);
359359
point._add(new maptalks.Point(size.width / 2, size.height / 2));
360360

361361
happen.once(eventContainer, {
362-
'type' : 'contextmenu',
363-
'clientX':point.x,
364-
'clientY':point.y
362+
'type': 'contextmenu',
363+
'clientX': point.x,
364+
'clientY': point.y
365365
});
366366

367367
polygon.endEdit();
@@ -370,8 +370,8 @@ describe('Geometry.Edit', function () {
370370

371371
it('update symbol when editing', function (done) {
372372
var circle = new maptalks.Circle(map.getCenter(), 1000, {
373-
symbol : {
374-
'polygonFill' : '#f00'
373+
symbol: {
374+
'polygonFill': '#f00'
375375
}
376376
}).addTo(layer);
377377
circle.startEdit();
@@ -384,11 +384,32 @@ describe('Geometry.Edit', function () {
384384
done();
385385
});
386386
circle.updateSymbol({
387-
'polygonFill' : '#ff0'
387+
'polygonFill': '#ff0'
388388
});
389389
});
390390
});
391391
});
392392

393+
it('polygon edit collision', function (done) {
394+
const c = 0.00001;
395+
const polygon = new maptalks.Polygon([[[0, 0], [c, 0], [c, c], [0, c], [0, 0]]])
396+
polygon.addTo(layer);
397+
398+
var spy = sinon.spy();
399+
polygon.on('handlecollision', spy);
400+
401+
map.setZoom(3);
402+
setTimeout(() => {
403+
polygon.startEdit({
404+
collision:true
405+
});
406+
setTimeout(() => {
407+
expect(spy.called).to.be.ok();
408+
polygon.endEdit();
409+
done();
410+
}, 100);
411+
}, 200);
412+
});
413+
393414

394415
});

0 commit comments

Comments
 (0)