|
303 | 303 | :is-creating-simple-mission="isCreatingSimpleMission" |
304 | 304 | :surveys="surveys" |
305 | 305 | :selected-survey-id="selectedSurveyId" |
| 306 | + :undo-is-in-progress="undoIsInProgress" |
| 307 | + :enable-undo="enableUndoForCurrentSurvey" |
306 | 308 | @close="hideContextMenu" |
307 | 309 | @delete-selected-survey="deleteSelectedSurvey" |
308 | 310 | @toggle-survey="toggleSurvey" |
309 | 311 | @toggle-simple-mission="toggleSimpleMission" |
| 312 | + @undo-generated-waypoints="undoGenerateWaypoints" |
310 | 313 | @regenerate-survey-waypoints="regenerateSurveyWaypoints" |
311 | 314 | @survey-lines-angle="onSurveyLinesAngleChange" |
312 | 315 | /> |
@@ -402,11 +405,22 @@ const selectedSurveyId = ref<string>('') |
402 | 405 | const surveyPolygonLayers = ref<{ [key: string]: Polygon }>({}) |
403 | 406 | const lastSelectedSurveyId = ref('') |
404 | 407 | const surveys = ref<Survey[]>([]) |
| 408 | +const canUndo = ref<Record<string, boolean>>({}) |
| 409 | +const undoIsInProgress = ref(false) |
| 410 | +const lastSurveyState = ref<Record<string, SurveyPolygon>>({}) |
405 | 411 | const isDragging = ref(false) |
406 | 412 | let dragStartLatLng: L.LatLng | null = null |
407 | 413 | let polygonLatLngsAtDragStart: L.LatLng[] = [] |
408 | 414 | let ignoreNextClick = false |
409 | 415 |
|
| 416 | +const enableUndoForCurrentSurvey = computed(() => { |
| 417 | + return ( |
| 418 | + surveys.value.length > 0 && |
| 419 | + selectedSurveyId.value === surveys.value[surveys.value.length - 1].id && |
| 420 | + canUndo.value[selectedSurveyId.value] |
| 421 | + ) |
| 422 | +}) |
| 423 | +
|
410 | 424 | const selectedSurvey = computed(() => { |
411 | 425 | return surveys.value.find((survey) => survey.id === selectedSurveyId.value) |
412 | 426 | }) |
@@ -547,6 +561,8 @@ const toggleSurvey = (): void => { |
547 | 561 | } |
548 | 562 | if (isCreatingSurvey.value) { |
549 | 563 | isCreatingSurvey.value = false |
| 564 | + lastSurveyState.value = {} |
| 565 | + canUndo.value = {} |
550 | 566 | return |
551 | 567 | } |
552 | 568 | isCreatingSurvey.value = true |
@@ -650,11 +666,18 @@ const handleKeyDown = (event: KeyboardEvent): void => { |
650 | 666 | if (event.key === 'Delete' && selectedSurveyId.value) { |
651 | 667 | deleteSelectedSurvey() |
652 | 668 | } |
| 669 | + if (event.ctrlKey && event.key.toLowerCase() === 'z' && enableUndoForCurrentSurvey.value && !undoIsInProgress.value) { |
| 670 | + console.log('Undo In Progress') |
| 671 | + undoGenerateWaypoints() |
| 672 | + event.preventDefault() |
| 673 | + } |
653 | 674 | } |
654 | 675 |
|
655 | 676 | const clearSurveyCreation = (): void => { |
656 | 677 | clearSurveyPath() |
657 | 678 | isCreatingSurvey.value = false |
| 679 | + lastSurveyState.value = {} |
| 680 | + canUndo.value = {} |
658 | 681 | } |
659 | 682 |
|
660 | 683 | const deleteSelectedSurvey = (): void => { |
@@ -699,6 +722,12 @@ const deleteSelectedSurvey = (): void => { |
699 | 722 | if (selectedSurveyId.value === surveyId) { |
700 | 723 | selectedSurveyId.value = surveys.value.length > 0 ? surveys.value[0].id : '' |
701 | 724 | } |
| 725 | + if (lastSurveyState.value[surveyId]) { |
| 726 | + delete lastSurveyState.value[surveyId] |
| 727 | + } |
| 728 | + if (canUndo.value[surveyId]) { |
| 729 | + delete canUndo.value[surveyId] |
| 730 | + } |
702 | 731 |
|
703 | 732 | showSnackbar({ variant: 'success', message: 'Survey deleted.', duration: 2000 }) |
704 | 733 | hideContextMenu() |
@@ -1089,12 +1118,17 @@ const generateWaypointsFromSurvey = (): void => { |
1089 | 1118 | } |
1090 | 1119 |
|
1091 | 1120 | const newSurveyId = uuid() |
| 1121 | + canUndo.value[newSurveyId] = true |
1092 | 1122 |
|
1093 | 1123 | const polygonCoordinates: WaypointCoordinates[] = surveyPolygonVertexesPositions.value.map((latLng) => [ |
1094 | 1124 | latLng.lat, |
1095 | 1125 | latLng.lng, |
1096 | 1126 | ]) |
1097 | 1127 |
|
| 1128 | + lastSurveyState.value[newSurveyId] = { |
| 1129 | + polygonPositions: polygonCoordinates, |
| 1130 | + } |
| 1131 | +
|
1098 | 1132 | const adjustedAngle = 90 - surveyLinesAngle.value |
1099 | 1133 | const continuousPath = generateSurveyPath( |
1100 | 1134 | surveyPolygonVertexesPositions.value, |
@@ -1269,6 +1303,108 @@ const createSurveyVertexMarker = ( |
1269 | 1303 | }) |
1270 | 1304 | } |
1271 | 1305 |
|
| 1306 | +const undoGenerateWaypoints = (): void => { |
| 1307 | + if (undoIsInProgress.value) return |
| 1308 | + contextMenuVisible.value = false |
| 1309 | + undoIsInProgress.value = true |
| 1310 | + const surveyId = selectedSurveyId.value |
| 1311 | +
|
| 1312 | + if (!surveyId || !canUndo.value[surveyId] || !lastSurveyState.value[surveyId]) { |
| 1313 | + showSnackbar({ variant: 'error', message: 'Nothing to undo.', duration: 2000 }) |
| 1314 | + undoIsInProgress.value = false |
| 1315 | + return |
| 1316 | + } |
| 1317 | +
|
| 1318 | + if (selectedSurvey.value) { |
| 1319 | + selectedSurvey.value.waypoints.forEach((waypoint) => { |
| 1320 | + const index = missionStore.currentPlanningWaypoints.findIndex((wp) => wp.id === waypoint.id) |
| 1321 | + if (index !== -1) { |
| 1322 | + missionStore.currentPlanningWaypoints.splice(index, 1) |
| 1323 | + } |
| 1324 | + const marker = waypointMarkers.value[waypoint.id] |
| 1325 | + if (marker) { |
| 1326 | + planningMap.value?.removeLayer(marker) |
| 1327 | + delete waypointMarkers.value[waypoint.id] |
| 1328 | + } |
| 1329 | + }) |
| 1330 | + } |
| 1331 | +
|
| 1332 | + planningMap.value?.eachLayer((layer) => { |
| 1333 | + if (layer instanceof L.Polyline && layer.options.className === 'waypoint-connection') { |
| 1334 | + planningMap.value?.removeLayer(layer) |
| 1335 | + } |
| 1336 | + }) |
| 1337 | +
|
| 1338 | + const index = surveys.value.findIndex((survey) => survey.id === surveyId) |
| 1339 | + if (index !== -1) { |
| 1340 | + surveys.value.splice(index, 1) |
| 1341 | + } |
| 1342 | + selectedSurveyId.value = '' |
| 1343 | +
|
| 1344 | + const surveyState = lastSurveyState.value[surveyId] |
| 1345 | + surveyPolygonVertexesPositions.value = surveyState.polygonPositions.map(([lat, lng]) => L.latLng(lat, lng)) |
| 1346 | +
|
| 1347 | + surveyPolygonVertexesMarkers.value.forEach((marker) => marker.remove()) |
| 1348 | + surveyPolygonVertexesMarkers.value = [] |
| 1349 | +
|
| 1350 | + surveyEdgeAddMarkers.forEach((marker) => marker.remove()) |
| 1351 | + surveyEdgeAddMarkers.length = 0 |
| 1352 | +
|
| 1353 | + if (surveyPolygonLayer.value) { |
| 1354 | + planningMap.value?.removeLayer(surveyPolygonLayer.value as unknown as L.Layer) |
| 1355 | + surveyPolygonLayer.value = null |
| 1356 | + } |
| 1357 | + if (surveyPathLayer.value) { |
| 1358 | + planningMap.value?.removeLayer(surveyPathLayer.value as unknown as L.Layer) |
| 1359 | + surveyPathLayer.value = null |
| 1360 | + } |
| 1361 | +
|
| 1362 | + surveyPolygonVertexesPositions.value.forEach((latLng) => { |
| 1363 | + const newMarker = createSurveyVertexMarker( |
| 1364 | + latLng, |
| 1365 | + // onClick callback |
| 1366 | + (marker) => { |
| 1367 | + const targetIndex = surveyPolygonVertexesMarkers.value.indexOf(marker) |
| 1368 | + if (targetIndex !== -1) { |
| 1369 | + surveyPolygonVertexesPositions.value.splice(targetIndex, 1) |
| 1370 | + surveyPolygonVertexesMarkers.value.splice(targetIndex, 1) |
| 1371 | + marker.remove() |
| 1372 | + updatePolygon() |
| 1373 | + updateSurveyEdgeAddMarkers() |
| 1374 | + createSurveyPath() |
| 1375 | + } |
| 1376 | + }, |
| 1377 | + // onDrag callback |
| 1378 | + () => { |
| 1379 | + updatePolygon() |
| 1380 | + createSurveyPath() |
| 1381 | + } |
| 1382 | + ).addTo(planningMap.value!) |
| 1383 | +
|
| 1384 | + surveyPolygonVertexesMarkers.value.push(newMarker) |
| 1385 | + }) |
| 1386 | +
|
| 1387 | + updateSurveyEdgeAddMarkers() |
| 1388 | +
|
| 1389 | + surveyPolygonLayer.value = L.polygon(surveyPolygonVertexesPositions.value, { |
| 1390 | + color: '#3B82F6', |
| 1391 | + fillColor: '#60A5FA', |
| 1392 | + fillOpacity: 0.2, |
| 1393 | + weight: 3, |
| 1394 | + className: 'survey-polygon', |
| 1395 | + }).addTo(planningMap.value!) |
| 1396 | +
|
| 1397 | + enablePolygonDragging() |
| 1398 | +
|
| 1399 | + delete lastSurveyState.value[surveyId] |
| 1400 | + delete canUndo.value[surveyId] |
| 1401 | + isCreatingSurvey.value = true |
| 1402 | +
|
| 1403 | + createSurveyPath() |
| 1404 | + showSnackbar({ variant: 'success', message: 'Undo successful.', duration: 1000 }) |
| 1405 | + undoIsInProgress.value = false |
| 1406 | +} |
| 1407 | +
|
1272 | 1408 | const addWaypointMarker = (waypoint: Waypoint): void => { |
1273 | 1409 | if (!planningMap.value) return |
1274 | 1410 |
|
@@ -1377,7 +1513,6 @@ onMounted(async () => { |
1377 | 1513 | }) |
1378 | 1514 |
|
1379 | 1515 | await goHome() |
1380 | | - await nextTick() |
1381 | 1516 |
|
1382 | 1517 | if (planningMap.value) { |
1383 | 1518 | planningMap.value.on('contextmenu', showContextMenu) |
|
0 commit comments