Implement snap to corner

This commit is contained in:
Fabio Giovanazzi 2025-08-02 13:09:19 +02:00
parent bfd08978a1
commit 6576da2660
No known key found for this signature in database
GPG key ID: 4BDF1B40A49FDD23

View file

@ -1870,20 +1870,16 @@ editor = {
// check if layer is within the area limit for infinite extension
var allowInfiniteExtension = editor._is_layer_in_extension_area(layer, latlng, mapPoint);
var snapPoint = editor._find_closest_point_on_geometry(layer, latlng, mapPoint, allowInfiniteExtension);
if (snapPoint && snapPoint.distance < editor._snap_distance) {
candidates.push(snapPoint);
}
var snapPoints = editor._find_closest_point_on_geometry(layer, latlng, mapPoint, allowInfiniteExtension);
candidates.push(...snapPoints);
});
// check current editing shape with infinite extension enabled
if (editor._current_editing_shape) {
var currentShapeSnap = editor._find_closest_point_on_geometry(
var currentShapeSnapPoints = editor._find_closest_point_on_geometry(
editor._current_editing_shape, latlng, mapPoint, true // Always enable infinite extension for current shape
);
if (currentShapeSnap && currentShapeSnap.distance < editor._snap_distance) {
candidates.push(currentShapeSnap);
}
candidates.push(...currentShapeSnapPoints);
}
// find closest candidate
@ -1891,6 +1887,30 @@ editor = {
candidates.sort(function(a, b) { return a.distance - b.distance; });
var best = candidates[0];
// see if we can snap to a corner, i.e. an edge intersection
if (candidates.length >= 2 && candidates[0].isLine && candidates[1].isLine) {
console.log(candidates.slice(0,2))
var inters = editor._intersect_infinite(
[candidates[0].edgeStart, candidates[0].edgeEnd],
[candidates[1].edgeStart, candidates[1].edgeEnd]
)
if (inters) {
intersMap = editor.map.latLngToContainerPoint(inters)
var distance = Math.sqrt(
Math.pow(intersMap.x - mapPoint.x, 2) +
Math.pow(intersMap.y - mapPoint.y, 2)
)
if (distance < editor._snap_distance) {
best = {
latlng: inters,
distance: distance,
referenceVertex: inters,
}
}
}
}
// show snap indicator with edge highlighting
editor._show_snap_indicator(best.latlng, best);
@ -1901,6 +1921,24 @@ editor = {
}
},
_intersect_infinite: function(line1, line2) {
const [p1, p2] = line1;
const [p3, p4] = line2;
const x1 = p1.lng, y1 = p1.lat;
const x2 = p2.lng, y2 = p2.lat;
const x3 = p3.lng, y3 = p3.lat;
const x4 = p4.lng, y4 = p4.lat;
const denom = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
if (denom === 0) return null; // parallel
const px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4)) / denom;
const py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4)) / denom;
return {lng: px, lat: py};
},
_is_layer_in_extension_area: function(layer, targetLatLng, targetMapPoint) {
if (!layer.getLatLngs) return false;
@ -2064,10 +2102,9 @@ editor = {
},
_find_closest_point_on_geometry: function(layer, targetLatLng, targetMapPoint, allowInfiniteExtension) {
if (!layer.getLatLngs) return null;
if (!layer.getLatLngs) return [];
var closestPoint = null;
var closestDistance = Infinity;
var closestPoints = [];
try {
var coordinates = [];
@ -2083,16 +2120,16 @@ editor = {
var centerMapPoint = editor.map.latLngToContainerPoint(center);
var distance = centerMapPoint.distanceTo(targetMapPoint);
if (distance < editor._snap_distance) {
return {
return [{
latlng: center,
distance: distance,
edgeStart: center,
edgeEnd: center,
isInfiniteExtension: false,
isRightAngle: false
};
}];
}
return null;
return [];
}
// check each edge of the geometry
@ -2102,17 +2139,16 @@ editor = {
var p2 = coordinates[(i + 1) % coordinates.length];
var snapPoint = editor._find_closest_point_on_edge(p1, p2, targetLatLng, targetMapPoint, allowInfiniteExtension);
if (snapPoint && snapPoint.distance < closestDistance) {
closestDistance = snapPoint.distance;
closestPoint = snapPoint;
if (snapPoint && snapPoint.distance < editor._snap_distance) {
closestPoints.push(snapPoint);
}
}
} catch (error) {
return null;
return [];
}
return closestPoint;
return closestPoints;
},
_find_closest_point_on_edge: function(p1, p2, targetLatLng, targetMapPoint, allowInfiniteExtension) {
@ -2132,6 +2168,7 @@ editor = {
distance: distance,
edgeStart: p1,
edgeEnd: p2,
isLine: true,
isInfiniteExtension: false,
isRightAngle: false
};
@ -2167,6 +2204,7 @@ editor = {
distance: distance,
edgeStart: p1,
edgeEnd: p2,
isLine: true,
isInfiniteExtension: isInfiniteExtension,
isRightAngle: false,
t: originalT
@ -2195,9 +2233,26 @@ editor = {
editor._show_90_degree_highlight(snapInfo);
} else if (snapInfo && snapInfo.edgeStart && snapInfo.edgeEnd) {
editor._show_edge_highlight(snapInfo);
} else if (snapInfo && snapInfo.referenceVertex) {
editor._show_intersect_highlight(snapInfo);
}
},
_show_intersect_highlight: function(snapInfo) {
var referenceVertex = snapInfo.referenceVertex;
var snapPoint = snapInfo.latlng;
// Draw line from reference vertex to snap point
var guideLine = L.polyline([referenceVertex, snapPoint], {
color: '#00aaff',
weight: 2,
opacity: 0.8,
dashArray: '4, 4',
className: '90-degree-guide'
});
editor._snap_indicator.addLayer(guideLine);
},
_show_90_degree_highlight: function(snapInfo) {
var referenceVertex = snapInfo.referenceVertex;
var snapPoint = snapInfo.latlng;