303303 :is-creating-simple-mission =" isCreatingSimpleMission"
304304 :surveys =" surveys"
305305 :selected-survey-id =" selectedSurveyId"
306+ :undo-is-in-progress =" undoIsInProgress"
307+ :enable-undo =" enableUndoForCurrentSurvey"
306308 @close =" hideContextMenu"
307309 @delete-selected-survey =" deleteSelectedSurvey"
308310 @toggle-survey =" toggleSurvey"
309311 @toggle-simple-mission =" toggleSimpleMission"
312+ @undo-generated-waypoints =" undoGenerateWaypoints"
310313 @regenerate-survey-waypoints =" regenerateSurveyWaypoints"
311314 @survey-lines-angle =" onSurveyLinesAngleChange"
312315 />
@@ -320,7 +323,7 @@ import { saveAs } from 'file-saver'
320323import L , { type LatLngTuple , LeafletMouseEvent , Map , Marker , Polygon } from ' leaflet'
321324import { v4 as uuid } from ' uuid'
322325import type { Ref } from ' vue'
323- import { computed , nextTick , onMounted , onUnmounted , ref , toRaw , watch } from ' vue'
326+ import { computed , onMounted , onUnmounted , ref , toRaw , watch } from ' vue'
324327
325328import ContextMenu from ' @/components/mission-planning/ContextMenu.vue'
326329import ScanDirectionDial from ' @/components/mission-planning/ScanDirectionDial.vue'
@@ -402,11 +405,22 @@ const selectedSurveyId = ref<string>('')
402405const surveyPolygonLayers = ref <{ [key : string ]: Polygon }>({})
403406const lastSelectedSurveyId = ref (' ' )
404407const surveys = ref <Survey []>([])
408+ const canUndo = ref <Record <string , boolean >>({})
409+ const undoIsInProgress = ref (false )
410+ const lastSurveyState = ref <Record <string , SurveyPolygon >>({})
405411const isDragging = ref (false )
406412let dragStartLatLng: L .LatLng | null = null
407413let polygonLatLngsAtDragStart: L .LatLng [] = []
408414let ignoreNextClick = false
409415
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+
410424const selectedSurvey = computed (() => {
411425 return surveys .value .find ((survey ) => survey .id === selectedSurveyId .value )
412426})
@@ -547,6 +561,8 @@ const toggleSurvey = (): void => {
547561 }
548562 if (isCreatingSurvey .value ) {
549563 isCreatingSurvey .value = false
564+ lastSurveyState .value = {}
565+ canUndo .value = {}
550566 return
551567 }
552568 isCreatingSurvey .value = true
@@ -650,11 +666,18 @@ const handleKeyDown = (event: KeyboardEvent): void => {
650666 if (event .key === ' Delete' && selectedSurveyId .value ) {
651667 deleteSelectedSurvey ()
652668 }
669+ if (event .ctrlKey && event .key .toLowerCase () === ' z' && enableUndoForCurrentSurvey .value && ! undoIsInProgress .value ) {
670+ console .log (' Undo In Progress' )
671+ undoGenerateWaypoints ()
672+ event .preventDefault ()
673+ }
653674}
654675
655676const clearSurveyCreation = (): void => {
656677 clearSurveyPath ()
657678 isCreatingSurvey .value = false
679+ lastSurveyState .value = {}
680+ canUndo .value = {}
658681}
659682
660683const deleteSelectedSurvey = (): void => {
@@ -699,6 +722,12 @@ const deleteSelectedSurvey = (): void => {
699722 if (selectedSurveyId .value === surveyId ) {
700723 selectedSurveyId .value = surveys .value .length > 0 ? surveys .value [0 ].id : ' '
701724 }
725+ if (lastSurveyState .value [surveyId ]) {
726+ delete lastSurveyState .value [surveyId ]
727+ }
728+ if (canUndo .value [surveyId ]) {
729+ delete canUndo .value [surveyId ]
730+ }
702731
703732 showSnackbar ({ variant: ' success' , message: ' Survey deleted.' , duration: 2000 })
704733 hideContextMenu ()
@@ -1089,12 +1118,17 @@ const generateWaypointsFromSurvey = (): void => {
10891118 }
10901119
10911120 const newSurveyId = uuid ()
1121+ canUndo .value [newSurveyId ] = true
10921122
10931123 const polygonCoordinates: WaypointCoordinates [] = surveyPolygonVertexesPositions .value .map ((latLng ) => [
10941124 latLng .lat ,
10951125 latLng .lng ,
10961126 ])
10971127
1128+ lastSurveyState .value [newSurveyId ] = {
1129+ polygonPositions: polygonCoordinates ,
1130+ }
1131+
10981132 const adjustedAngle = 90 - surveyLinesAngle .value
10991133 const continuousPath = generateSurveyPath (
11001134 surveyPolygonVertexesPositions .value ,
@@ -1269,6 +1303,108 @@ const createSurveyVertexMarker = (
12691303 })
12701304}
12711305
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+
12721408const addWaypointMarker = (waypoint : Waypoint ): void => {
12731409 if (! planningMap .value ) return
12741410
@@ -1377,7 +1513,6 @@ onMounted(async () => {
13771513 })
13781514
13791515 await goHome ()
1380- await nextTick ()
13811516
13821517 if (planningMap .value ) {
13831518 planningMap .value .on (' contextmenu' , showContextMenu )
0 commit comments