Merge branch 'ours' of https://repos.hackathon.bz.it/2025-summer/team-3 into ours
This commit is contained in:
commit
2c8b75b2ab
5 changed files with 480 additions and 75 deletions
|
@ -3,7 +3,7 @@ set -e
|
|||
python3 -m venv .env
|
||||
source ./.env/bin/activate
|
||||
pip install -r src/requirements/production.txt -r src/requirements/dev.txt
|
||||
pip install psycopg2 pylibmc
|
||||
pip install psycopg2
|
||||
sudo pacman -Sy librsvg
|
||||
|
||||
./start_db.sh db
|
||||
|
|
|
@ -15,7 +15,7 @@ x-healthcheck_defaults: &healthcheck_defaults
|
|||
interval: 10s
|
||||
timeout: 2s
|
||||
retries: 5
|
||||
start_period: 5s
|
||||
start_period: 10s
|
||||
x-c3nav-defaults: &c3nav-defaults
|
||||
build:
|
||||
context: ..
|
||||
|
@ -81,7 +81,7 @@ services:
|
|||
image: postgres:16
|
||||
healthcheck:
|
||||
<<: *healthcheck_defaults
|
||||
test: pg_isready -U postgres
|
||||
test: pg_isready -U postgres && psql -U postgres -d ${C3NAV_DATABASE_NAME:-c3nav} -c "SELECT 1;"
|
||||
environment:
|
||||
POSTGRES_DB: ${C3NAV_DATABASE_NAME:-c3nav}
|
||||
POSTGRES_HOST_AUTH_METHOD: "trust"
|
||||
|
|
|
@ -12,17 +12,13 @@ docker volume create c3nav-redis
|
|||
|
||||
# Start only postgres and redis first (no build since we pre-built)
|
||||
docker compose up -d postgres redis
|
||||
sleep 10
|
||||
|
||||
|
||||
sleep 5
|
||||
cat ./db/auth_user.sql | docker exec -i local_run-postgres-1 su - postgres -c 'psql c3nav'
|
||||
|
||||
# Create Django superuser
|
||||
|
||||
sleep 1
|
||||
|
||||
# Load database dump before starting the main app
|
||||
cat ./db/dump.sql | docker exec -i local_run-postgres-1 su - postgres -c 'psql c3nav'
|
||||
cat ./db/dump.sql | docker exec -i local_run-postgres-1 su - postgres -c 'psql c3nav' > /dev/null
|
||||
|
||||
# Fix geometry access permissions for anonymous users
|
||||
docker exec -i local_run-postgres-1 psql -U postgres -d c3nav -c "UPDATE mapdata_space SET base_mapdata_accessible = true;"
|
||||
|
@ -38,7 +34,6 @@ user.set_password('admin')
|
|||
user.save()
|
||||
print('Password set successfully for user:', user.username)
|
||||
\" | /app/env/bin/python manage.py shell"
|
||||
sleep 30
|
||||
|
||||
|
||||
# Fake apply all migrations since we loaded from dump
|
||||
|
|
|
@ -607,3 +607,71 @@ label.theme-color-label {
|
|||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes snap-pulse {
|
||||
0% {
|
||||
opacity: 0.8;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
100% {
|
||||
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();
|
||||
|
||||
|
@ -1458,7 +1458,7 @@ editor = {
|
|||
editor._editing_layer.disableEdit();
|
||||
editor._editing_layer = null;
|
||||
}
|
||||
|
||||
|
||||
// Clear snap indicators when canceling editing
|
||||
editor._clear_snap_indicators();
|
||||
},
|
||||
|
@ -1675,36 +1675,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;
|
||||
};
|
||||
|
@ -1717,61 +1714,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);
|
||||
}
|
||||
});
|
||||
|
||||
// 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 the 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();
|
||||
|
@ -1779,99 +1794,296 @@ 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();
|
||||
|
||||
var size = 0.001; // adjust this to control square size
|
||||
|
@ -1890,6 +2102,136 @@ editor = {
|
|||
});
|
||||
|
||||
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