improve snap-to-edge
This commit is contained in:
parent
603329fbda
commit
f3acc31a1c
2 changed files with 465 additions and 69 deletions
|
@ -619,4 +619,57 @@ label.theme-color-label {
|
|||
opacity: 0.8;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Edge highlight styles for snap-to-edges */
|
||||
.edge-highlight {
|
||||
z-index: 999;
|
||||
pointer-events: none;
|
||||
animation: edge-fade-in 0.2s ease-in;
|
||||
}
|
||||
|
||||
.original-edge-highlight {
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
animation: edge-fade-in 0.2s ease-in;
|
||||
}
|
||||
|
||||
@keyframes edge-fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
/* Right-angle snap indicators */
|
||||
.right-angle-reference {
|
||||
z-index: 998;
|
||||
pointer-events: none;
|
||||
animation: edge-fade-in 0.2s ease-in;
|
||||
}
|
||||
|
||||
.right-angle-line {
|
||||
z-index: 1001;
|
||||
pointer-events: none;
|
||||
animation: right-angle-pulse 2s infinite;
|
||||
}
|
||||
|
||||
.right-angle-square {
|
||||
z-index: 1002;
|
||||
pointer-events: none;
|
||||
animation: right-angle-pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes right-angle-pulse {
|
||||
0% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
|
@ -182,7 +182,7 @@ editor = {
|
|||
|
||||
$('#sidebar').addClass('loading').find('.content').html('');
|
||||
editor._cancel_editing();
|
||||
|
||||
|
||||
// Clear snap indicators when unloading
|
||||
editor._clear_snap_indicators();
|
||||
},
|
||||
|
@ -1448,7 +1448,7 @@ editor = {
|
|||
editor._editing_layer.disableEdit();
|
||||
editor._editing_layer = null;
|
||||
}
|
||||
|
||||
|
||||
// Clear snap indicators when canceling editing
|
||||
editor._clear_snap_indicators();
|
||||
},
|
||||
|
@ -1665,36 +1665,33 @@ editor = {
|
|||
|
||||
// Snap-to-edges functionality
|
||||
_snap_enabled: true,
|
||||
_snap_distance: 15, // pixels
|
||||
_snap_to_base_map: false,
|
||||
_snap_distance: 30, // pixels
|
||||
_extension_area_multiplier: 4, // Extension area = snap_distance * this multiplier
|
||||
_snap_to_base_map: false,
|
||||
_snap_indicator: null,
|
||||
_snap_candidates: [],
|
||||
|
||||
init_snap_to_edges: function() {
|
||||
// Initialize snap indicator layer
|
||||
editor._snap_indicator = L.layerGroup().addTo(editor.map);
|
||||
|
||||
// Override existing drawing event handlers to include snapping
|
||||
|
||||
editor.map.on('editable:drawing:move', editor._handle_snap_during_draw);
|
||||
editor.map.on('editable:vertex:drag', editor._handle_snap_during_vertex_drag);
|
||||
|
||||
// Add snap toggle to UI
|
||||
|
||||
editor._add_snap_controls();
|
||||
},
|
||||
|
||||
_add_snap_controls: function() {
|
||||
// Add snap toggle control to the map
|
||||
var snapControl = L.control({position: 'topleft'});
|
||||
snapControl.onAdd = function() {
|
||||
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-snap');
|
||||
container.innerHTML = '<a href="#" title="Toggle Snap to Edges" class="snap-toggle ' +
|
||||
container.innerHTML = '<a href="#" title="Toggle Snap to Edges" class="snap-toggle ' +
|
||||
(editor._snap_enabled ? 'active' : '') + '">⚡</a>';
|
||||
|
||||
|
||||
L.DomEvent.on(container.querySelector('.snap-toggle'), 'click', function(e) {
|
||||
e.preventDefault();
|
||||
editor._toggle_snap();
|
||||
});
|
||||
|
||||
|
||||
L.DomEvent.disableClickPropagation(container);
|
||||
return container;
|
||||
};
|
||||
|
@ -1707,61 +1704,79 @@ editor = {
|
|||
if (toggle) {
|
||||
toggle.classList.toggle('active', editor._snap_enabled);
|
||||
}
|
||||
// Clear any existing snap indicators
|
||||
|
||||
editor._clear_snap_indicators();
|
||||
},
|
||||
|
||||
_handle_snap_during_draw: function(e) {
|
||||
if (!editor._snap_enabled || !editor._creating) return;
|
||||
|
||||
|
||||
var snapped = editor._find_and_apply_snap(e.latlng);
|
||||
if (snapped) {
|
||||
e.latlng.lat = snapped.lat;
|
||||
e.latlng.lng = snapped.lng;
|
||||
}
|
||||
|
||||
// Apply existing rounding
|
||||
|
||||
// Apply rounding
|
||||
e.latlng.lat = Math.round(e.latlng.lat * 100) / 100;
|
||||
e.latlng.lng = Math.round(e.latlng.lng * 100) / 100;
|
||||
},
|
||||
|
||||
_handle_snap_during_vertex_drag: function(e) {
|
||||
if (!editor._snap_enabled) return;
|
||||
|
||||
|
||||
var snapped = editor._find_and_apply_snap(e.latlng);
|
||||
if (snapped) {
|
||||
e.latlng.lat = snapped.lat;
|
||||
e.latlng.lng = snapped.lng;
|
||||
}
|
||||
|
||||
// Apply existing rounding and other constraints
|
||||
|
||||
e.vertex.setLatLng([Math.round(e.latlng.lat * 100) / 100, Math.round(e.latlng.lng * 100) / 100]);
|
||||
},
|
||||
|
||||
_find_and_apply_snap: function(latlng) {
|
||||
if (!editor._geometries_layer) return null;
|
||||
|
||||
|
||||
var mapPoint = editor.map.latLngToContainerPoint(latlng);
|
||||
var candidates = [];
|
||||
|
||||
// Find snap candidates from existing geometries
|
||||
|
||||
// check for right-angle snap to current shape vertices
|
||||
var rightAngleSnap = editor._find_right_angle_snap(latlng, mapPoint);
|
||||
if (rightAngleSnap) {
|
||||
candidates.push(rightAngleSnap);
|
||||
}
|
||||
|
||||
// find snap candidates from existing geometries with area-limited infinite extension
|
||||
editor._geometries_layer.eachLayer(function(layer) {
|
||||
if (layer === editor._editing_layer) return; // Don't snap to self
|
||||
|
||||
var snapPoint = editor._find_closest_point_on_geometry(layer, latlng, mapPoint);
|
||||
if (layer === editor._editing_layer) return; // don't snap to self
|
||||
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
|
||||
// Find the closest candidate
|
||||
|
||||
// check current editing shape with infinite extension enabled
|
||||
if (editor._current_editing_shape) {
|
||||
var currentShapeSnap = 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);
|
||||
}
|
||||
}
|
||||
|
||||
// find closest candidate
|
||||
if (candidates.length > 0) {
|
||||
candidates.sort(function(a, b) { return a.distance - b.distance; });
|
||||
var best = candidates[0];
|
||||
|
||||
// Show snap indicator
|
||||
editor._show_snap_indicator(best.latlng);
|
||||
|
||||
|
||||
// show snap indicator with edge highlighting
|
||||
editor._show_snap_indicator(best.latlng, best);
|
||||
|
||||
return best.latlng;
|
||||
} else {
|
||||
editor._clear_snap_indicators();
|
||||
|
@ -1769,100 +1784,298 @@ editor = {
|
|||
}
|
||||
},
|
||||
|
||||
_find_closest_point_on_geometry: function(layer, targetLatLng, targetMapPoint) {
|
||||
if (!layer.getLatLngs) return null;
|
||||
|
||||
var closestPoint = null;
|
||||
var closestDistance = Infinity;
|
||||
|
||||
_is_layer_in_extension_area: function(layer, targetLatLng, targetMapPoint) {
|
||||
if (!layer.getLatLngs) return false;
|
||||
|
||||
// skip circles entirely for infinite extension
|
||||
if (layer instanceof L.Circle || layer instanceof L.CircleMarker) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
var coordinates = [];
|
||||
|
||||
// Handle different geometry types
|
||||
|
||||
if (layer instanceof L.Polygon || layer instanceof L.Polyline) {
|
||||
coordinates = layer.getLatLngs();
|
||||
if (coordinates[0] && Array.isArray(coordinates[0])) {
|
||||
coordinates = coordinates[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (coordinates.length === 0) return false;
|
||||
|
||||
// extension area radius (in pixels)
|
||||
var extensionAreaRadius = editor._snap_distance * editor._extension_area_multiplier;
|
||||
|
||||
// check if any vertex of the layer is within the extension area
|
||||
for (var i = 0; i < coordinates.length; i++) {
|
||||
var vertexMapPoint = editor.map.latLngToContainerPoint(coordinates[i]);
|
||||
var distanceToVertex = vertexMapPoint.distanceTo(targetMapPoint);
|
||||
|
||||
if (distanceToVertex <= extensionAreaRadius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < coordinates.length; i++) {
|
||||
var p1 = coordinates[i];
|
||||
var p2 = coordinates[(i + 1) % coordinates.length];
|
||||
|
||||
if (editor._edge_intersects_circle(p1, p2, targetLatLng, targetMapPoint, extensionAreaRadius)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_edge_intersects_circle: function(edgeStart, edgeEnd, circleCenter, circleCenterMap, radius) {
|
||||
var p1Map = editor.map.latLngToContainerPoint(edgeStart);
|
||||
var p2Map = editor.map.latLngToContainerPoint(edgeEnd);
|
||||
|
||||
// find closest point on edge to circle center
|
||||
var dx = p2Map.x - p1Map.x;
|
||||
var dy = p2Map.y - p1Map.y;
|
||||
var length = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (length === 0) {
|
||||
return p1Map.distanceTo(circleCenterMap) <= radius;
|
||||
}
|
||||
|
||||
var t = Math.max(0, Math.min(1,
|
||||
((circleCenterMap.x - p1Map.x) * dx + (circleCenterMap.y - p1Map.y) * dy) / (length * length)
|
||||
));
|
||||
|
||||
var closestPoint = {
|
||||
x: p1Map.x + t * dx,
|
||||
y: p1Map.y + t * dy
|
||||
};
|
||||
|
||||
var distance = Math.sqrt(
|
||||
Math.pow(closestPoint.x - circleCenterMap.x, 2) +
|
||||
Math.pow(closestPoint.y - circleCenterMap.y, 2)
|
||||
);
|
||||
|
||||
return distance <= radius;
|
||||
},
|
||||
|
||||
_find_closest_point_on_geometry: function(layer, targetLatLng, targetMapPoint, allowInfiniteExtension) {
|
||||
if (!layer.getLatLngs) return null;
|
||||
|
||||
var closestPoint = null;
|
||||
var closestDistance = Infinity;
|
||||
|
||||
try {
|
||||
var coordinates = [];
|
||||
|
||||
// handle different geometry types
|
||||
if (layer instanceof L.Polygon || layer instanceof L.Polyline) {
|
||||
coordinates = layer.getLatLngs();
|
||||
if (coordinates[0] && Array.isArray(coordinates[0])) {
|
||||
coordinates = coordinates[0]; // Handle polygon with holes
|
||||
}
|
||||
} else if (layer instanceof L.Circle || layer instanceof L.CircleMarker) {
|
||||
// For circles, snap to center
|
||||
var center = layer.getLatLng();
|
||||
var centerMapPoint = editor.map.latLngToContainerPoint(center);
|
||||
var distance = centerMapPoint.distanceTo(targetMapPoint);
|
||||
if (distance < editor._snap_distance) {
|
||||
return {
|
||||
latlng: center,
|
||||
distance: distance
|
||||
distance: distance,
|
||||
edgeStart: center,
|
||||
edgeEnd: center,
|
||||
isInfiniteExtension: false,
|
||||
isRightAngle: false
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check each edge of the geometry
|
||||
|
||||
// check each edge of the geometry
|
||||
for (var i = 0; i < coordinates.length; i++) {
|
||||
var p1 = coordinates[i];
|
||||
var p2 = coordinates[(i + 1) % coordinates.length];
|
||||
|
||||
var snapPoint = editor._find_closest_point_on_edge(p1, p2, targetLatLng, targetMapPoint);
|
||||
|
||||
var snapPoint = editor._find_closest_point_on_edge(p1, p2, targetLatLng, targetMapPoint, allowInfiniteExtension);
|
||||
if (snapPoint && snapPoint.distance < closestDistance) {
|
||||
closestDistance = snapPoint.distance;
|
||||
closestPoint = snapPoint;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
// Silently handle geometry access errors
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return closestPoint;
|
||||
},
|
||||
|
||||
_find_closest_point_on_edge: function(p1, p2, targetLatLng, targetMapPoint) {
|
||||
_find_closest_point_on_edge: function(p1, p2, targetLatLng, targetMapPoint, allowInfiniteExtension) {
|
||||
var p1Map = editor.map.latLngToContainerPoint(p1);
|
||||
var p2Map = editor.map.latLngToContainerPoint(p2);
|
||||
|
||||
// Find closest point on line segment
|
||||
|
||||
// find closest point on line (infinite or segment based on allowInfiniteExtension)
|
||||
var dx = p2Map.x - p1Map.x;
|
||||
var dy = p2Map.y - p1Map.y;
|
||||
var length = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
|
||||
if (length === 0) {
|
||||
// Points are the same, snap to the point
|
||||
// points are the same, snap to the point
|
||||
var distance = p1Map.distanceTo(targetMapPoint);
|
||||
return {
|
||||
latlng: p1,
|
||||
distance: distance
|
||||
distance: distance,
|
||||
edgeStart: p1,
|
||||
edgeEnd: p2,
|
||||
isInfiniteExtension: false,
|
||||
isRightAngle: false
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate parameter t for closest point on line
|
||||
|
||||
// calculate parameter t for closest point on line
|
||||
var t = ((targetMapPoint.x - p1Map.x) * dx + (targetMapPoint.y - p1Map.y) * dy) / (length * length);
|
||||
t = Math.max(0, Math.min(1, t)); // Clamp to line segment
|
||||
|
||||
// Calculate closest point
|
||||
|
||||
// clamp t based on allowInfiniteExtension
|
||||
var originalT = t;
|
||||
if (!allowInfiniteExtension) {
|
||||
t = Math.max(0, Math.min(1, t)); // Clamp to line segment
|
||||
}
|
||||
|
||||
// calculate closest point
|
||||
var closestMapPoint = {
|
||||
x: p1Map.x + t * dx,
|
||||
y: p1Map.y + t * dy
|
||||
};
|
||||
|
||||
|
||||
var distance = Math.sqrt(
|
||||
Math.pow(closestMapPoint.x - targetMapPoint.x, 2) +
|
||||
Math.pow(closestMapPoint.x - targetMapPoint.x, 2) +
|
||||
Math.pow(closestMapPoint.y - targetMapPoint.y, 2)
|
||||
);
|
||||
|
||||
// Convert back to lat/lng
|
||||
|
||||
var closestLatLng = editor.map.containerPointToLatLng(closestMapPoint);
|
||||
|
||||
|
||||
// determine if this is an infinite extension
|
||||
var isInfiniteExtension = allowInfiniteExtension && (originalT < 0 || originalT > 1);
|
||||
|
||||
return {
|
||||
latlng: closestLatLng,
|
||||
distance: distance
|
||||
distance: distance,
|
||||
edgeStart: p1,
|
||||
edgeEnd: p2,
|
||||
isInfiniteExtension: isInfiniteExtension,
|
||||
isRightAngle: false,
|
||||
t: originalT
|
||||
};
|
||||
},
|
||||
|
||||
_show_snap_indicator: function(latlng) {
|
||||
_find_right_angle_snap: function(targetLatLng, targetMapPoint) {
|
||||
if (!editor._current_editing_shape) return null;
|
||||
|
||||
try {
|
||||
var coordinates = [];
|
||||
|
||||
if (editor._current_editing_shape.getLatLngs) {
|
||||
coordinates = editor._current_editing_shape.getLatLngs();
|
||||
if (coordinates[0] && Array.isArray(coordinates[0])) {
|
||||
coordinates = coordinates[0]; // Handle polygon with holes
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (coordinates.length < 2) return null;
|
||||
|
||||
var bestRightAngleSnap = null;
|
||||
var closestDistance = Infinity;
|
||||
|
||||
// Check each vertex for potential right-angle formation
|
||||
for (var i = 0; i < coordinates.length; i++) {
|
||||
var vertex = coordinates[i];
|
||||
var vertexMap = editor.map.latLngToContainerPoint(vertex);
|
||||
|
||||
var distanceToVertex = vertexMap.distanceTo(targetMapPoint);
|
||||
if (distanceToVertex > editor._snap_distance * 2) continue; // Larger radius for right-angle detection
|
||||
|
||||
var prevVertex = coordinates[(i - 1 + coordinates.length) % coordinates.length];
|
||||
var nextVertex = coordinates[(i + 1) % coordinates.length];
|
||||
|
||||
var rightAngleSnap1 = editor._calculate_right_angle_snap(vertex, prevVertex, targetLatLng, targetMapPoint);
|
||||
if (rightAngleSnap1 && rightAngleSnap1.distance < closestDistance) {
|
||||
closestDistance = rightAngleSnap1.distance;
|
||||
bestRightAngleSnap = rightAngleSnap1;
|
||||
}
|
||||
|
||||
var rightAngleSnap2 = editor._calculate_right_angle_snap(vertex, nextVertex, targetLatLng, targetMapPoint);
|
||||
if (rightAngleSnap2 && rightAngleSnap2.distance < closestDistance) {
|
||||
closestDistance = rightAngleSnap2.distance;
|
||||
bestRightAngleSnap = rightAngleSnap2;
|
||||
}
|
||||
}
|
||||
|
||||
return bestRightAngleSnap;
|
||||
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
_calculate_right_angle_snap: function(vertex, adjacentVertex, targetLatLng, targetMapPoint) {
|
||||
var vertexMap = editor.map.latLngToContainerPoint(vertex);
|
||||
var adjacentMap = editor.map.latLngToContainerPoint(adjacentVertex);
|
||||
|
||||
var edgeDx = adjacentMap.x - vertexMap.x;
|
||||
var edgeDy = adjacentMap.y - vertexMap.y;
|
||||
var edgeLength = Math.sqrt(edgeDx * edgeDx + edgeDy * edgeDy);
|
||||
|
||||
if (edgeLength === 0) return null;
|
||||
|
||||
var perpDx = -edgeDy / edgeLength;
|
||||
var perpDy = edgeDx / edgeLength;
|
||||
|
||||
var targetDx = targetMapPoint.x - vertexMap.x;
|
||||
var targetDy = targetMapPoint.y - vertexMap.y;
|
||||
var targetLength = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
|
||||
|
||||
if (targetLength === 0) return null;
|
||||
|
||||
var projectionLength = targetDx * perpDx + targetDy * perpDy;
|
||||
|
||||
var rightAngleMapPoint = {
|
||||
x: vertexMap.x + projectionLength * perpDx,
|
||||
y: vertexMap.y + projectionLength * perpDy
|
||||
};
|
||||
|
||||
var distance = Math.sqrt(
|
||||
Math.pow(rightAngleMapPoint.x - targetMapPoint.x, 2) +
|
||||
Math.pow(rightAngleMapPoint.y - targetMapPoint.y, 2)
|
||||
);
|
||||
|
||||
if (distance < editor._snap_distance && Math.abs(projectionLength) > 10) { // minimum 10 pixels away from vertex
|
||||
var rightAngleLatLng = editor.map.containerPointToLatLng(rightAngleMapPoint);
|
||||
|
||||
return {
|
||||
latlng: rightAngleLatLng,
|
||||
distance: distance,
|
||||
edgeStart: vertex,
|
||||
edgeEnd: rightAngleLatLng,
|
||||
isInfiniteExtension: false,
|
||||
isRightAngle: true,
|
||||
rightAngleVertex: vertex,
|
||||
adjacentVertex: adjacentVertex
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_show_snap_indicator: function(latlng, snapInfo) {
|
||||
editor._clear_snap_indicators();
|
||||
|
||||
|
||||
// snap point indicator
|
||||
var indicator = L.circleMarker(latlng, {
|
||||
radius: 4,
|
||||
color: '#ff6b6b',
|
||||
|
@ -1871,8 +2084,138 @@ editor = {
|
|||
weight: 2,
|
||||
className: 'snap-indicator'
|
||||
});
|
||||
|
||||
|
||||
editor._snap_indicator.addLayer(indicator);
|
||||
|
||||
if (snapInfo && snapInfo.edgeStart && snapInfo.edgeEnd) {
|
||||
editor._show_edge_highlight(snapInfo);
|
||||
}
|
||||
},
|
||||
|
||||
_show_edge_highlight: function(snapInfo) {
|
||||
if (!snapInfo.edgeStart || !snapInfo.edgeEnd) return;
|
||||
|
||||
// handle right-angle visualization
|
||||
if (snapInfo.isRightAngle) {
|
||||
editor._show_right_angle_highlight(snapInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
var startPoint = snapInfo.edgeStart;
|
||||
var endPoint = snapInfo.edgeEnd;
|
||||
var extendedStart, extendedEnd;
|
||||
|
||||
if (snapInfo.isInfiniteExtension && snapInfo.t !== undefined) {
|
||||
// Extend the line significantly beyond the original edge
|
||||
var startMap = editor.map.latLngToContainerPoint(startPoint);
|
||||
var endMap = editor.map.latLngToContainerPoint(endPoint);
|
||||
|
||||
var dx = endMap.x - startMap.x;
|
||||
var dy = endMap.y - startMap.y;
|
||||
var length = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (length > 0) {
|
||||
dx /= length;
|
||||
dy /= length;
|
||||
|
||||
var extensionDistance = 1000;
|
||||
var extStartMap = {
|
||||
x: startMap.x - dx * extensionDistance,
|
||||
y: startMap.y - dy * extensionDistance
|
||||
};
|
||||
var extEndMap = {
|
||||
x: endMap.x + dx * extensionDistance,
|
||||
y: endMap.y + dy * extensionDistance
|
||||
};
|
||||
|
||||
extendedStart = editor.map.containerPointToLatLng(extStartMap);
|
||||
extendedEnd = editor.map.containerPointToLatLng(extEndMap);
|
||||
} else {
|
||||
extendedStart = startPoint;
|
||||
extendedEnd = endPoint;
|
||||
}
|
||||
} else {
|
||||
extendedStart = startPoint;
|
||||
extendedEnd = endPoint;
|
||||
}
|
||||
|
||||
// create edge highlight line
|
||||
var edgeHighlight = L.polyline([extendedStart, extendedEnd], {
|
||||
color: snapInfo.isInfiniteExtension ? '#ffaa00' : '#66dd66', // Orange for infinite, green for original edge
|
||||
weight: snapInfo.isInfiniteExtension ? 2 : 3,
|
||||
opacity: snapInfo.isInfiniteExtension ? 0.4 : 0.6,
|
||||
dashArray: snapInfo.isInfiniteExtension ? '8, 4' : null, // Dashed for infinite extension
|
||||
className: 'edge-highlight'
|
||||
});
|
||||
|
||||
editor._snap_indicator.addLayer(edgeHighlight);
|
||||
|
||||
// if it's an infinite extension, also show the original edge segment more prominently
|
||||
if (snapInfo.isInfiniteExtension) {
|
||||
var originalEdge = L.polyline([startPoint, endPoint], {
|
||||
color: '#66dd66',
|
||||
weight: 3,
|
||||
opacity: 0.8,
|
||||
className: 'original-edge-highlight'
|
||||
});
|
||||
editor._snap_indicator.addLayer(originalEdge);
|
||||
}
|
||||
},
|
||||
|
||||
_show_right_angle_highlight: function(snapInfo) {
|
||||
var vertex = snapInfo.rightAngleVertex;
|
||||
var adjacentVertex = snapInfo.adjacentVertex;
|
||||
var rightAnglePoint = snapInfo.latlng;
|
||||
|
||||
var referenceEdge = L.polyline([vertex, adjacentVertex], {
|
||||
color: '#4488ff',
|
||||
weight: 2,
|
||||
opacity: 0.6,
|
||||
className: 'right-angle-reference'
|
||||
});
|
||||
editor._snap_indicator.addLayer(referenceEdge);
|
||||
|
||||
var rightAngleLine = L.polyline([vertex, rightAnglePoint], {
|
||||
color: '#ff4488',
|
||||
weight: 3,
|
||||
opacity: 0.8,
|
||||
dashArray: '6, 3',
|
||||
className: 'right-angle-line'
|
||||
});
|
||||
editor._snap_indicator.addLayer(rightAngleLine);
|
||||
|
||||
var vertexMap = editor.map.latLngToContainerPoint(vertex);
|
||||
var adjacentMap = editor.map.latLngToContainerPoint(adjacentVertex);
|
||||
var rightAngleMap = editor.map.latLngToContainerPoint(rightAnglePoint);
|
||||
|
||||
var size = 15; // Square size in pixels
|
||||
var dx1 = adjacentMap.x - vertexMap.x;
|
||||
var dy1 = adjacentMap.y - vertexMap.y;
|
||||
var len1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
|
||||
|
||||
var dx2 = rightAngleMap.x - vertexMap.x;
|
||||
var dy2 = rightAngleMap.y - vertexMap.y;
|
||||
var len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
||||
|
||||
if (len1 > 0 && len2 > 0) {
|
||||
dx1 = (dx1 / len1) * size;
|
||||
dy1 = (dy1 / len1) * size;
|
||||
dx2 = (dx2 / len2) * size;
|
||||
dy2 = (dy2 / len2) * size;
|
||||
|
||||
var corner1 = editor.map.containerPointToLatLng({x: vertexMap.x + dx1, y: vertexMap.y + dy1});
|
||||
var corner2 = editor.map.containerPointToLatLng({x: vertexMap.x + dx1 + dx2, y: vertexMap.y + dy1 + dy2});
|
||||
var corner3 = editor.map.containerPointToLatLng({x: vertexMap.x + dx2, y: vertexMap.y + dy2});
|
||||
|
||||
var rightAngleSquare = L.polyline([vertex, corner1, corner2, corner3, vertex], {
|
||||
color: '#ff4488',
|
||||
weight: 2,
|
||||
opacity: 0.7,
|
||||
fill: false,
|
||||
className: 'right-angle-square'
|
||||
});
|
||||
editor._snap_indicator.addLayer(rightAngleSquare);
|
||||
}
|
||||
},
|
||||
|
||||
_clear_snap_indicators: function() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue