redo undo 작업중
This commit is contained in:
parent
d4766e30a3
commit
77e3f688c6
@ -9,6 +9,7 @@ import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
|||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { normalizeDigits } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
export default function AuxiliaryEdit(props) {
|
export default function AuxiliaryEdit(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -22,6 +23,7 @@ export default function AuxiliaryEdit(props) {
|
|||||||
const [arrow2, setArrow2] = useState(null)
|
const [arrow2, setArrow2] = useState(null)
|
||||||
const currentObject = useRecoilValue(currentObjectState)
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if ((!arrow1 && !arrow2) || (+verticalSize === 0 && +horizonSize === 0)) {
|
if ((!arrow1 && !arrow2) || (+verticalSize === 0 && +horizonSize === 0)) {
|
||||||
swalFire({ title: getMessage('length.direction.is.required'), type: 'alert' })
|
swalFire({ title: getMessage('length.direction.is.required'), type: 'alert' })
|
||||||
@ -38,6 +40,7 @@ export default function AuxiliaryEdit(props) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveSnapshot()
|
||||||
if (type === 'copy') {
|
if (type === 'copy') {
|
||||||
if (currentObject) {
|
if (currentObject) {
|
||||||
copy(
|
copy(
|
||||||
@ -87,14 +90,12 @@ export default function AuxiliaryEdit(props) {
|
|||||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow1('↑')
|
setArrow1('↑')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow1('↓')
|
setArrow1('↓')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
@ -121,14 +122,12 @@ export default function AuxiliaryEdit(props) {
|
|||||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow2('←')
|
setArrow2('←')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow2('→')
|
setArrow2('→')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import Big from 'big.js'
|
import Big from 'big.js'
|
||||||
import { calcLineActualSize, calcLinePlaneSize } from '@/util/qpolygon-utils'
|
import { calcLineActualSize, calcLinePlaneSize } from '@/util/qpolygon-utils'
|
||||||
import { normalizeDigits } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||||
|
|
||||||
export default function AuxiliarySize(props) {
|
export default function AuxiliarySize(props) {
|
||||||
@ -53,7 +54,9 @@ export default function AuxiliarySize(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
|
saveSnapshot()
|
||||||
const { x1, y1, x2, y2 } = currentObject
|
const { x1, y1, x2, y2 } = currentObject
|
||||||
|
|
||||||
// 선택한 선분의 planeSize와 actualSize의 사잇각을 구한다.
|
// 선택한 선분의 planeSize와 actualSize의 사잇각을 구한다.
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { canvasState } from '@/store/canvasAtom'
|
|||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
import { useModule } from '@/hooks/module/useModule'
|
import { useModule } from '@/hooks/module/useModule'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
export const PANEL_EDIT_TYPE = {
|
export const PANEL_EDIT_TYPE = {
|
||||||
MOVE: 'move',
|
MOVE: 'move',
|
||||||
@ -30,6 +31,7 @@ export default function PanelEdit(props) {
|
|||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { moduleMove, moduleCopy, moduleMultiMove, moduleMultiCopy, moduleMoveAll, moduleCopyAll } = useModule()
|
const { moduleMove, moduleCopy, moduleMultiMove, moduleMultiCopy, moduleMoveAll, moduleCopyAll } = useModule()
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module') // selectedObj에 없는 객체만 필터링
|
const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module') // selectedObj에 없는 객체만 필터링
|
||||||
@ -52,6 +54,7 @@ export default function PanelEdit(props) {
|
|||||||
|
|
||||||
//모듈 이동 적용
|
//모듈 이동 적용
|
||||||
const handleApply = () => {
|
const handleApply = () => {
|
||||||
|
saveSnapshot()
|
||||||
if (length <= 0) {
|
if (length <= 0) {
|
||||||
swalFire({
|
swalFire({
|
||||||
title: getMessage('common.message.please.input.over', [1]),
|
title: getMessage('common.message.please.input.over', [1]),
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||||
@ -14,7 +15,9 @@ export default function ColumnInsert(props) {
|
|||||||
const [selectedType, setSelectedType] = useState(MODULE_INSERT_TYPE.LEFT)
|
const [selectedType, setSelectedType] = useState(MODULE_INSERT_TYPE.LEFT)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { moduleColumnInsert } = useModule()
|
const { moduleColumnInsert } = useModule()
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const handleApply = () => {
|
const handleApply = () => {
|
||||||
|
saveSnapshot()
|
||||||
moduleColumnInsert(selectedType)
|
moduleColumnInsert(selectedType)
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||||
@ -14,6 +15,7 @@ export default function ColumnRemove(props) {
|
|||||||
const [selectedType, setSelectedType] = useState(MODULE_REMOVE_TYPE.LEFT)
|
const [selectedType, setSelectedType] = useState(MODULE_REMOVE_TYPE.LEFT)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { moduleColumnRemove } = useModule()
|
const { moduleColumnRemove } = useModule()
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const types = [
|
const types = [
|
||||||
{ name: getMessage('modal.panel.column.remove.type.left'), value: MODULE_REMOVE_TYPE.LEFT },
|
{ name: getMessage('modal.panel.column.remove.type.left'), value: MODULE_REMOVE_TYPE.LEFT },
|
||||||
{ name: getMessage('modal.panel.column.remove.type.right'), value: MODULE_REMOVE_TYPE.RIGHT },
|
{ name: getMessage('modal.panel.column.remove.type.right'), value: MODULE_REMOVE_TYPE.RIGHT },
|
||||||
@ -21,6 +23,7 @@ export default function ColumnRemove(props) {
|
|||||||
{ name: getMessage('modal.panel.column.remove.type.none'), value: MODULE_REMOVE_TYPE.NONE },
|
{ name: getMessage('modal.panel.column.remove.type.none'), value: MODULE_REMOVE_TYPE.NONE },
|
||||||
]
|
]
|
||||||
const handleApply = () => {
|
const handleApply = () => {
|
||||||
|
saveSnapshot()
|
||||||
moduleColumnRemove(selectedType)
|
moduleColumnRemove(selectedType)
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
@ -14,7 +15,9 @@ export default function RowInsert(props) {
|
|||||||
const [selectedType, setSelectedType] = useState(MODULE_INSERT_TYPE.TOP)
|
const [selectedType, setSelectedType] = useState(MODULE_INSERT_TYPE.TOP)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { muduleRowInsert } = useModule()
|
const { muduleRowInsert } = useModule()
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const handleApply = () => {
|
const handleApply = () => {
|
||||||
|
saveSnapshot()
|
||||||
muduleRowInsert(selectedType)
|
muduleRowInsert(selectedType)
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
@ -14,6 +15,7 @@ export default function RowRemove(props) {
|
|||||||
const [selectedType, setSelectedType] = useState(MODULE_REMOVE_TYPE.TOP)
|
const [selectedType, setSelectedType] = useState(MODULE_REMOVE_TYPE.TOP)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { moduleRowRemove } = useModule()
|
const { moduleRowRemove } = useModule()
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const types = [
|
const types = [
|
||||||
{ name: getMessage('modal.row.remove.type.up'), value: MODULE_REMOVE_TYPE.TOP },
|
{ name: getMessage('modal.row.remove.type.up'), value: MODULE_REMOVE_TYPE.TOP },
|
||||||
{ name: getMessage('modal.row.remove.type.down'), value: MODULE_REMOVE_TYPE.BOTTOM },
|
{ name: getMessage('modal.row.remove.type.down'), value: MODULE_REMOVE_TYPE.BOTTOM },
|
||||||
@ -21,6 +23,7 @@ export default function RowRemove(props) {
|
|||||||
{ name: getMessage('modal.row.remove.type.none'), value: MODULE_REMOVE_TYPE.NONE },
|
{ name: getMessage('modal.row.remove.type.none'), value: MODULE_REMOVE_TYPE.NONE },
|
||||||
]
|
]
|
||||||
const handleApply = () => {
|
const handleApply = () => {
|
||||||
|
saveSnapshot()
|
||||||
// if (apply) apply()
|
// if (apply) apply()
|
||||||
moduleRowRemove(selectedType)
|
moduleRowRemove(selectedType)
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { MENU } from '@/common/common'
|
|||||||
import { useTrestle } from '@/hooks/module/useTrestle'
|
import { useTrestle } from '@/hooks/module/useTrestle'
|
||||||
import { useOrientation } from '@/hooks/module/useOrientation'
|
import { useOrientation } from '@/hooks/module/useOrientation'
|
||||||
import { corridorDimensionSelector } from '@/store/settingAtom'
|
import { corridorDimensionSelector } from '@/store/settingAtom'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 처리 훅
|
* 메뉴 처리 훅
|
||||||
@ -36,6 +37,7 @@ export default function useMenu() {
|
|||||||
const { clear: trestleClear, setAllModuleSurfaceIsComplete } = useTrestle()
|
const { clear: trestleClear, setAllModuleSurfaceIsComplete } = useTrestle()
|
||||||
const { nextStep } = useOrientation()
|
const { nextStep } = useOrientation()
|
||||||
const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
|
const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const handleMenu = (type) => {
|
const handleMenu = (type) => {
|
||||||
closeAll()
|
closeAll()
|
||||||
if (type === 'outline') {
|
if (type === 'outline') {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { useLine } from '@/hooks/useLine'
|
|||||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { useText } from '@/hooks/useText'
|
import { useText } from '@/hooks/useText'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
const ROOF_COLOR = {
|
const ROOF_COLOR = {
|
||||||
0: 'rgb(199,240,213)',
|
0: 'rgb(199,240,213)',
|
||||||
@ -26,6 +27,7 @@ export function useRoofFn() {
|
|||||||
const { setPolygonLinesActualSize } = usePolygon()
|
const { setPolygonLinesActualSize } = usePolygon()
|
||||||
const { changeCorridorDimensionText } = useText()
|
const { changeCorridorDimensionText } = useText()
|
||||||
const [outerLinePoints, setOuterLinePoints] = useRecoilState(outerLinePointsState)
|
const [outerLinePoints, setOuterLinePoints] = useRecoilState(outerLinePointsState)
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
|
|
||||||
//면형상 선택 클릭시 지붕 패턴 입히기
|
//면형상 선택 클릭시 지붕 패턴 입히기
|
||||||
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) {
|
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) {
|
||||||
@ -236,6 +238,9 @@ export function useRoofFn() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
canvas.on('mouse:down', (event) => {
|
canvas.on('mouse:down', (event) => {
|
||||||
|
// checkPolygon 을 캔버스에서 제거한 뒤 스냅샷을 찍어야 빨간 사각형이 포함되지 않음
|
||||||
|
canvas.remove(checkPolygon)
|
||||||
|
saveSnapshot()
|
||||||
let mousePos = canvas.getPointer(event.e)
|
let mousePos = canvas.getPointer(event.e)
|
||||||
mousePos = { x: Math.round(mousePos.x), y: Math.round(mousePos.y) }
|
mousePos = { x: Math.round(mousePos.x), y: Math.round(mousePos.y) }
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentObjectState, pitchTextSelector } from '@/store/canvasAtom'
|
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentObjectState, pitchTextSelector } from '@/store/canvasAtom'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||||
|
import { currentMenuState } from '@/store/canvasAtom'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useLine } from '@/hooks/useLine'
|
import { useLine } from '@/hooks/useLine'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useEvent } from '@/hooks/useEvent'
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
import { LINE_TYPE, MENU, POLYGON_TYPE } from '@/common/common'
|
||||||
import { useMode } from '@/hooks/useMode'
|
import { useMode } from '@/hooks/useMode'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
@ -20,6 +21,7 @@ export function useRoofShapePassivitySetting(id) {
|
|||||||
SHED: 'shed',
|
SHED: 'shed',
|
||||||
}
|
}
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
|
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||||
const pitchText = useRecoilValue(pitchTextSelector)
|
const pitchText = useRecoilValue(pitchTextSelector)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -194,10 +196,11 @@ export function useRoofShapePassivitySetting(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
saveSnapshot()
|
saveSnapshot(null, { force: true })
|
||||||
isFix.current = true
|
isFix.current = true
|
||||||
handleLineToPolygon()
|
handleLineToPolygon()
|
||||||
|
// 수동설정 완료 → 지붕면 할당 메뉴로 전환 (undo 가드 해제)
|
||||||
|
setCurrentMenu(MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC)
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import { useLine } from '@/hooks/useLine'
|
|||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import Big from 'big.js'
|
import Big from 'big.js'
|
||||||
|
import { calcLinePlaneSize } from '@/util/qpolygon-utils'
|
||||||
import { outerLineFixState } from '@/store/outerLineAtom'
|
import { outerLineFixState } from '@/store/outerLineAtom'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
// 외벽선 편집 및 오프셋
|
// 외벽선 편집 및 오프셋
|
||||||
export function useWallLineOffsetSetting(id) {
|
export function useWallLineOffsetSetting(id) {
|
||||||
@ -17,7 +19,7 @@ export function useWallLineOffsetSetting(id) {
|
|||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||||
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const wallLineEditRef = useRef(null)
|
const wallLineEditRef = useRef(null)
|
||||||
const length1Ref = useRef(null)
|
const length1Ref = useRef(null)
|
||||||
const length2Ref = useRef(null)
|
const length2Ref = useRef(null)
|
||||||
@ -46,7 +48,13 @@ export function useWallLineOffsetSetting(id) {
|
|||||||
direction: direction,
|
direction: direction,
|
||||||
})
|
})
|
||||||
|
|
||||||
line.attributes = { ...currentWallLineRef.current.attributes }
|
// 분할된 라인의 실제 길이로 planeSize/actualSize 재계산
|
||||||
|
const newPlaneSize = calcLinePlaneSize({ x1: point1.x, y1: point1.y, x2: point2.x, y2: point2.y })
|
||||||
|
line.attributes = {
|
||||||
|
...currentWallLineRef.current.attributes,
|
||||||
|
planeSize: newPlaneSize,
|
||||||
|
actualSize: newPlaneSize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TYPES = {
|
const TYPES = {
|
||||||
@ -197,9 +205,9 @@ export function useWallLineOffsetSetting(id) {
|
|||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (currentWallLineRef.current === null) {
|
if (currentWallLineRef.current === null) {
|
||||||
// alert('보조선을 먼저 선택하세요')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
saveSnapshot()
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TYPES.WALL_LINE_EDIT:
|
case TYPES.WALL_LINE_EDIT:
|
||||||
handleWallLineEditSave()
|
handleWallLineEditSave()
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import { useSurfaceShapeBatch } from './useSurfaceShapeBatch'
|
|||||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||||
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
||||||
import { useObject } from '@/hooks/useObject'
|
import { useObject } from '@/hooks/useObject'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
// 배치면 그리기
|
// 배치면 그리기
|
||||||
export function usePlacementShapeDrawing(id) {
|
export function usePlacementShapeDrawing(id) {
|
||||||
@ -48,6 +49,7 @@ export function usePlacementShapeDrawing(id) {
|
|||||||
const { setSurfaceShapePattern } = useRoofFn()
|
const { setSurfaceShapePattern } = useRoofFn()
|
||||||
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
||||||
const { handleSelectableObjects } = useObject()
|
const { handleSelectableObjects } = useObject()
|
||||||
|
const { saveSnapshot, popLastSnapshot } = useUndoRedo()
|
||||||
|
|
||||||
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
||||||
const adsorptionRange = useRecoilValue(adsorptionRangeState)
|
const adsorptionRange = useRecoilValue(adsorptionRangeState)
|
||||||
@ -92,13 +94,19 @@ export function usePlacementShapeDrawing(id) {
|
|||||||
}, [verticalHorizontalMode, points, adsorptionRange, adsorptionPointMode])
|
}, [verticalHorizontalMode, points, adsorptionRange, adsorptionPointMode])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// 배치면 그리기 시작 시 스냅샷 저장 (undo 하면 그리기 전 상태로 복원)
|
||||||
|
saveSnapshot()
|
||||||
setPoints([])
|
setPoints([])
|
||||||
setType(OUTER_LINE_TYPE.OUTER_LINE)
|
setType(OUTER_LINE_TYPE.OUTER_LINE)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const placementShapeDrawingStartPoint = canvas.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint')
|
const placementShapeDrawingStartPoint = canvas.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint')
|
||||||
|
|
||||||
canvas.remove(placementShapeDrawingStartPoint)
|
canvas.remove(placementShapeDrawingStartPoint)
|
||||||
|
|
||||||
|
// 확정하지 않고 닫은 경우 스냅샷 제거
|
||||||
|
if (!isFix.current) {
|
||||||
|
popLastSnapshot()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { usePopup } from '../usePopup'
|
|||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { useSwal } from '../useSwal'
|
import { useSwal } from '../useSwal'
|
||||||
import { useMessage } from '../useMessage'
|
import { useMessage } from '../useMessage'
|
||||||
|
import { useUndoRedo } from '../useUndoRedo'
|
||||||
|
|
||||||
const LINE_COLOR = {
|
const LINE_COLOR = {
|
||||||
EAVES: '#45CD7D',
|
EAVES: '#45CD7D',
|
||||||
@ -23,6 +24,7 @@ export function useRoofLinePropertySetting(props) {
|
|||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
const { saveSnapshot, popLastSnapshot } = useUndoRedo()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentObject && currentObject.name === 'cloneRoofLine') {
|
if (currentObject && currentObject.name === 'cloneRoofLine') {
|
||||||
@ -47,10 +49,20 @@ export function useRoofLinePropertySetting(props) {
|
|||||||
}
|
}
|
||||||
}, [currentObject])
|
}, [currentObject])
|
||||||
|
|
||||||
|
const isFixed = useRef(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// 팝업 열릴 때 스냅샷 저장
|
||||||
|
saveSnapshot()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'cloneRoofLine'))
|
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'cloneRoofLine'))
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
|
|
||||||
|
// 설정 완료하지 않고 닫은 경우 스냅샷 제거
|
||||||
|
if (!isFixed.current) {
|
||||||
|
popLastSnapshot()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@ -149,8 +161,6 @@ export function useRoofLinePropertySetting(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleFix = () => {
|
const handleFix = () => {
|
||||||
// const roof = canvas.getObjects().find((obj) => currentObject.parentId === obj.id)
|
|
||||||
// const notSettingLines = roof.lines.filter(
|
|
||||||
const notSettingLines = canvas
|
const notSettingLines = canvas
|
||||||
.getObjects()
|
.getObjects()
|
||||||
.filter((obj) => obj.name === 'cloneRoofLine')
|
.filter((obj) => obj.name === 'cloneRoofLine')
|
||||||
@ -187,6 +197,7 @@ export function useRoofLinePropertySetting(props) {
|
|||||||
// })
|
// })
|
||||||
// })
|
// })
|
||||||
|
|
||||||
|
isFixed.current = true
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
if (setIsHidden) setIsHidden(false)
|
if (setIsHidden) setIsHidden(false)
|
||||||
|
|||||||
@ -52,6 +52,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
const { saveSnapshot } = useUndoRedo()
|
const { saveSnapshot } = useUndoRedo()
|
||||||
|
|
||||||
const applySurfaceShape = (surfaceRefs, selectedType, id) => {
|
const applySurfaceShape = (surfaceRefs, selectedType, id) => {
|
||||||
|
saveSnapshot()
|
||||||
let length1, length2, length3, length4, length5
|
let length1, length2, length3, length4, length5
|
||||||
const surfaceId = selectedType?.id
|
const surfaceId = selectedType?.id
|
||||||
const azimuth = surfaceRefs.azimuth.current
|
const azimuth = surfaceRefs.azimuth.current
|
||||||
@ -844,6 +845,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
text: getMessage('batch.canvas.delete.all'),
|
text: getMessage('batch.canvas.delete.all'),
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: () => {
|
confirmFn: () => {
|
||||||
|
saveSnapshot()
|
||||||
canvas.clear()
|
canvas.clear()
|
||||||
delete canvas.outerLineFix
|
delete canvas.outerLineFix
|
||||||
|
|
||||||
|
|||||||
@ -155,6 +155,7 @@ export function useCanvas(id) {
|
|||||||
|
|
||||||
// 해당 오브젝트 타입의 경우 저장한 값 그대로 불러와야함
|
// 해당 오브젝트 타입의 경우 저장한 값 그대로 불러와야함
|
||||||
OBJECT_PROTOTYPE.forEach((type) => {
|
OBJECT_PROTOTYPE.forEach((type) => {
|
||||||
|
const originalToObject = type.toObject
|
||||||
type.toObject = function (propertiesToInclude) {
|
type.toObject = function (propertiesToInclude) {
|
||||||
let source = {}
|
let source = {}
|
||||||
|
|
||||||
@ -175,7 +176,9 @@ export function useCanvas(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), source)
|
// 원본 toObject 를 호출하여 Group 의 objects 등 고유 직렬화 로직을 유지
|
||||||
|
const base = originalToObject ? originalToObject.call(this, propertiesToInclude) : this.callSuper('toObject', propertiesToInclude)
|
||||||
|
return fabric.util.object.extend(base, source)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export function useCanvasEvent() {
|
|||||||
const currentMenu = useRecoilValue(currentMenuState)
|
const currentMenu = useRecoilValue(currentMenuState)
|
||||||
const { changeCorridorDimensionText } = useText()
|
const { changeCorridorDimensionText } = useText()
|
||||||
const { setPolygonLinesActualSize } = usePolygon()
|
const { setPolygonLinesActualSize } = usePolygon()
|
||||||
const { undo, redo } = useUndoRedo()
|
const { undo, redo, saveSnapshot } = useUndoRedo()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
canvas?.setZoom(canvasZoom / 100)
|
canvas?.setZoom(canvasZoom / 100)
|
||||||
@ -291,22 +291,18 @@ export function useCanvasEvent() {
|
|||||||
break
|
break
|
||||||
case 'Down': // IE/Edge에서 사용되는 값
|
case 'Down': // IE/Edge에서 사용되는 값
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
// "아래 화살표" 키가 눌렸을 때의 동작입니다.
|
|
||||||
moveDown()
|
moveDown()
|
||||||
break
|
break
|
||||||
case 'Up': // IE/Edge에서 사용되는 값
|
case 'Up': // IE/Edge에서 사용되는 값
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
// "위 화살표" 키가 눌렸을 때의 동작입니다.
|
|
||||||
moveUp()
|
moveUp()
|
||||||
break
|
break
|
||||||
case 'Left': // IE/Edge에서 사용되는 값
|
case 'Left': // IE/Edge에서 사용되는 값
|
||||||
case 'ArrowLeft':
|
case 'ArrowLeft':
|
||||||
// "왼쪽 화살표" 키가 눌렸을 때의 동작입니다.
|
|
||||||
moveLeft()
|
moveLeft()
|
||||||
break
|
break
|
||||||
case 'Right': // IE/Edge에서 사용되는 값
|
case 'Right': // IE/Edge에서 사용되는 값
|
||||||
case 'ArrowRight':
|
case 'ArrowRight':
|
||||||
// "오른쪽 화살표" 키가 눌렸을 때의 동작입니다.
|
|
||||||
moveRight()
|
moveRight()
|
||||||
break
|
break
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
|
|||||||
@ -43,6 +43,7 @@ import { useTrestle } from './module/useTrestle'
|
|||||||
import { useCircuitTrestle } from './useCirCuitTrestle'
|
import { useCircuitTrestle } from './useCirCuitTrestle'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { useText } from '@/hooks/useText'
|
import { useText } from '@/hooks/useText'
|
||||||
|
import { useUndoRedo } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
export function useContextMenu() {
|
export function useContextMenu() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
@ -77,6 +78,7 @@ export function useContextMenu() {
|
|||||||
const { isExistCircuit } = useCircuitTrestle()
|
const { isExistCircuit } = useCircuitTrestle()
|
||||||
const { changeCorridorDimensionText } = useText()
|
const { changeCorridorDimensionText } = useText()
|
||||||
const { setPolygonLinesActualSize, drawDirectionArrow } = usePolygon()
|
const { setPolygonLinesActualSize, drawDirectionArrow } = usePolygon()
|
||||||
|
const { saveSnapshot } = useUndoRedo()
|
||||||
const currentMenuSetting = () => {
|
const currentMenuSetting = () => {
|
||||||
switch (selectedMenu) {
|
switch (selectedMenu) {
|
||||||
case 'outline':
|
case 'outline':
|
||||||
@ -170,7 +172,10 @@ export function useContextMenu() {
|
|||||||
{
|
{
|
||||||
id: 'roofMaterialRemoveAll',
|
id: 'roofMaterialRemoveAll',
|
||||||
name: getMessage('contextmenu.roof.material.remove.all'),
|
name: getMessage('contextmenu.roof.material.remove.all'),
|
||||||
fn: () => removeAllRoofMaterial(),
|
fn: () => {
|
||||||
|
saveSnapshot()
|
||||||
|
removeAllRoofMaterial()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'selectMove',
|
id: 'selectMove',
|
||||||
@ -183,6 +188,7 @@ export function useContextMenu() {
|
|||||||
id: 'wallLineRemove',
|
id: 'wallLineRemove',
|
||||||
name: getMessage('contextmenu.wallline.remove'),
|
name: getMessage('contextmenu.wallline.remove'),
|
||||||
fn: (currentMousePos) => {
|
fn: (currentMousePos) => {
|
||||||
|
saveSnapshot()
|
||||||
removeOuterLines(currentMousePos)
|
removeOuterLines(currentMousePos)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -260,7 +266,10 @@ export function useContextMenu() {
|
|||||||
{
|
{
|
||||||
id: 'roofMaterialRemoveAll',
|
id: 'roofMaterialRemoveAll',
|
||||||
name: getMessage('contextmenu.roof.material.remove.all'),
|
name: getMessage('contextmenu.roof.material.remove.all'),
|
||||||
fn: () => removeAllRoofMaterial(),
|
fn: () => {
|
||||||
|
saveSnapshot()
|
||||||
|
removeAllRoofMaterial()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'selectMove',
|
id: 'selectMove',
|
||||||
@ -368,6 +377,7 @@ export function useContextMenu() {
|
|||||||
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
|
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
|
||||||
fn: () => {
|
fn: () => {
|
||||||
if (!currentObject) return
|
if (!currentObject) return
|
||||||
|
saveSnapshot()
|
||||||
const roof = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0]
|
const roof = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0]
|
||||||
if (!roof) {
|
if (!roof) {
|
||||||
// 아직 innerLines로 세팅이 안되어있는 line인 경우 제거
|
// 아직 innerLines로 세팅이 안되어있는 line인 경우 제거
|
||||||
@ -386,6 +396,7 @@ export function useContextMenu() {
|
|||||||
name: getMessage('contextmenu.auxiliary.vertical.bisector'),
|
name: getMessage('contextmenu.auxiliary.vertical.bisector'),
|
||||||
fn: () => {
|
fn: () => {
|
||||||
if (!currentObject) return
|
if (!currentObject) return
|
||||||
|
saveSnapshot()
|
||||||
const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1)
|
const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1)
|
||||||
const length = currentObject.length
|
const length = currentObject.length
|
||||||
|
|
||||||
@ -435,6 +446,7 @@ export function useContextMenu() {
|
|||||||
swalFire({ text: getMessage('roof.is.not.selected') })
|
swalFire({ text: getMessage('roof.is.not.selected') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
saveSnapshot()
|
||||||
const innerLines = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0]?.innerLines
|
const innerLines = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0]?.innerLines
|
||||||
if (innerLines) {
|
if (innerLines) {
|
||||||
innerLines.forEach((line) => {
|
innerLines.forEach((line) => {
|
||||||
@ -702,7 +714,10 @@ export function useContextMenu() {
|
|||||||
{
|
{
|
||||||
id: 'remove',
|
id: 'remove',
|
||||||
name: getMessage('contextmenu.remove'),
|
name: getMessage('contextmenu.remove'),
|
||||||
fn: () => modulesRemove(),
|
fn: () => {
|
||||||
|
saveSnapshot()
|
||||||
|
modulesRemove()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'move',
|
id: 'move',
|
||||||
@ -900,6 +915,7 @@ export function useContextMenu() {
|
|||||||
id: 'moduleRemove',
|
id: 'moduleRemove',
|
||||||
name: getMessage('contextmenu.module.remove'),
|
name: getMessage('contextmenu.module.remove'),
|
||||||
fn: () => {
|
fn: () => {
|
||||||
|
saveSnapshot()
|
||||||
moduleRoofRemove(currentObject.arrayData ?? [currentObject])
|
moduleRoofRemove(currentObject.arrayData ?? [currentObject])
|
||||||
|
|
||||||
// const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
|
// const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import {
|
|||||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||||
import { selectedMenuState } from '@/store/menuAtom'
|
import { selectedMenuState } from '@/store/menuAtom'
|
||||||
import { pendingPopupState, popupState } from '@/store/popupAtom'
|
import { pendingPopupState, popupState } from '@/store/popupAtom'
|
||||||
import { POLYGON_TYPE, SAVE_KEY } from '@/common/common'
|
import { MENU, POLYGON_TYPE, SAVE_KEY } from '@/common/common'
|
||||||
|
|
||||||
const MAX_HISTORY = 3
|
const MAX_HISTORY = 3
|
||||||
|
|
||||||
@ -182,6 +182,20 @@ export function useUndoRedo() {
|
|||||||
surface.modules = surfaceModules
|
surface.modules = surfaceModules
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// fabric.Group 내부의 QPolygon/QLine 자식에 canvas 참조 설정
|
||||||
|
// loadFromJSON 후 Group 자식은 canvas 를 받지 못해 init/addLengthText 등이 실패할 수 있음
|
||||||
|
objects
|
||||||
|
.filter((obj) => obj.type === 'group')
|
||||||
|
.forEach((group) => {
|
||||||
|
if (group._objects) {
|
||||||
|
group._objects.forEach((child) => {
|
||||||
|
if (!child.canvas) {
|
||||||
|
child.canvas = canvas
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 부모가 없는 고아 lengthText, arrow, flowText 제거
|
// 부모가 없는 고아 lengthText, arrow, flowText 제거
|
||||||
const parentIds = new Set(objects.filter((obj) => obj.id).map((obj) => obj.id))
|
const parentIds = new Set(objects.filter((obj) => obj.id).map((obj) => obj.id))
|
||||||
const orphans = objects.filter(
|
const orphans = objects.filter(
|
||||||
@ -258,6 +272,26 @@ export function useUndoRedo() {
|
|||||||
|
|
||||||
const cells = roofs.filter((obj) => obj.cells && obj.cells.length > 0)
|
const cells = roofs.filter((obj) => obj.cells && obj.cells.length > 0)
|
||||||
setDrewRoofCells(cells)
|
setDrewRoofCells(cells)
|
||||||
|
|
||||||
|
// loadFromJSON 후 QPolygon/QLine 의 lengthText 가 orphan 제거되었을 수 있으므로 재생성
|
||||||
|
// (initLines 로 새 QLine 이 만들어지면 기존 lengthText 의 parentId 가 안 맞음)
|
||||||
|
const qPolygons = objects.filter((obj) => obj.type === 'QPolygon' && obj.name !== 'arrow' && obj.name !== POLYGON_TYPE.MODULE && obj.name !== POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.name !== POLYGON_TYPE.OBJECT_SURFACE)
|
||||||
|
qPolygons.forEach((polygon) => {
|
||||||
|
try {
|
||||||
|
polygon.initLines()
|
||||||
|
polygon.addLengthText()
|
||||||
|
} catch (e) { /* skip */ }
|
||||||
|
})
|
||||||
|
|
||||||
|
canvas.getObjects()
|
||||||
|
.filter((obj) => obj.type === 'QLine')
|
||||||
|
.forEach((line) => {
|
||||||
|
try {
|
||||||
|
if (typeof line.addLengthText === 'function') {
|
||||||
|
line.addLengthText()
|
||||||
|
}
|
||||||
|
} catch (e) { /* skip */ }
|
||||||
|
})
|
||||||
}, [canvas, rebuildObjectGraph])
|
}, [canvas, rebuildObjectGraph])
|
||||||
|
|
||||||
const restoreAtomSnapshot = useCallback((atomSnapshot) => {
|
const restoreAtomSnapshot = useCallback((atomSnapshot) => {
|
||||||
@ -277,7 +311,6 @@ export function useUndoRedo() {
|
|||||||
if (!canvas) return null
|
if (!canvas) return null
|
||||||
try {
|
try {
|
||||||
const rawJSON = canvas.toJSON(SAVE_KEY)
|
const rawJSON = canvas.toJSON(SAVE_KEY)
|
||||||
// 순환 참조 제거 후 다시 파싱하여 순수 데이터 객체로 변환
|
|
||||||
return JSON.parse(safeStringify(rawJSON))
|
return JSON.parse(safeStringify(rawJSON))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Canvas snapshot failed:', e)
|
console.warn('Canvas snapshot failed:', e)
|
||||||
@ -285,9 +318,12 @@ export function useUndoRedo() {
|
|||||||
}
|
}
|
||||||
}, [canvas])
|
}, [canvas])
|
||||||
|
|
||||||
const saveSnapshot = useCallback((atomOverrides = null) => {
|
const saveSnapshot = useCallback((atomOverrides = null, { force = false } = {}) => {
|
||||||
if (!canvas || _isRestoring) return
|
if (!canvas || _isRestoring) return
|
||||||
|
|
||||||
|
// 지붕형상 수동설정 메뉴에서는 스냅샷을 쌓지 않는다 (force 시 예외)
|
||||||
|
if (!force && currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||||
|
|
||||||
// snapshot 전에 mouseLine 제거
|
// snapshot 전에 mouseLine 제거
|
||||||
const mouseLines = canvas.getObjects().filter((obj) => obj.name === 'mouseLine')
|
const mouseLines = canvas.getObjects().filter((obj) => obj.name === 'mouseLine')
|
||||||
mouseLines.forEach((obj) => canvas.remove(obj))
|
mouseLines.forEach((obj) => canvas.remove(obj))
|
||||||
@ -315,21 +351,28 @@ export function useUndoRedo() {
|
|||||||
return newStack
|
return newStack
|
||||||
})
|
})
|
||||||
setRedoStack([])
|
setRedoStack([])
|
||||||
}, [canvas, getCanvasSnapshot, getAtomSnapshot])
|
}, [canvas, currentMenu, getCanvasSnapshot, getAtomSnapshot])
|
||||||
|
|
||||||
const closeAllPopups = useCallback(() => {
|
const closeAllPopups = useCallback(() => {
|
||||||
setPopup({ config: [], other: [] })
|
setPopup((prev) => ({
|
||||||
|
// 처마 케라바 변경 팝업(EavesGableEdit)은 undo/redo 시 닫지 않는다
|
||||||
|
config: prev.config.filter((p) => p.component?.type?.name === 'EavesGableEdit'),
|
||||||
|
other: prev.other.filter((p) => p.component?.type?.name === 'EavesGableEdit'),
|
||||||
|
}))
|
||||||
}, [setPopup])
|
}, [setPopup])
|
||||||
|
|
||||||
const undo = useCallback(() => {
|
const undo = useCallback(() => {
|
||||||
|
// 지붕형상 수동설정 중에는 undo 차단
|
||||||
|
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||||
|
|
||||||
const currentUndoStack = undoStackRef.current
|
const currentUndoStack = undoStackRef.current
|
||||||
console.log('currentUndoStack', currentUndoStack)
|
|
||||||
if (!canvas || currentUndoStack.length === 0 || _isRestoring) return
|
if (!canvas || currentUndoStack.length === 0 || _isRestoring) return
|
||||||
|
|
||||||
_isRestoring = true
|
_isRestoring = true
|
||||||
|
|
||||||
const currentCanvasJSON = getCanvasSnapshot()
|
const currentCanvasJSON = getCanvasSnapshot()
|
||||||
const currentAtomSnapshot = getAtomSnapshot()
|
const currentAtomSnapshot = getAtomSnapshot()
|
||||||
|
|
||||||
const currentSnapshot = {
|
const currentSnapshot = {
|
||||||
canvasJSON: currentCanvasJSON,
|
canvasJSON: currentCanvasJSON,
|
||||||
atomSnapshot: currentAtomSnapshot,
|
atomSnapshot: currentAtomSnapshot,
|
||||||
@ -348,6 +391,9 @@ export function useUndoRedo() {
|
|||||||
}, [canvas, currentMenu, getCanvasSnapshot, getAtomSnapshot, restoreAtomSnapshot, rebuildCanvasAtoms, closeAllPopups])
|
}, [canvas, currentMenu, getCanvasSnapshot, getAtomSnapshot, restoreAtomSnapshot, rebuildCanvasAtoms, closeAllPopups])
|
||||||
|
|
||||||
const redo = useCallback(() => {
|
const redo = useCallback(() => {
|
||||||
|
// 지붕형상 수동설정 중에는 redo 차단
|
||||||
|
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||||
|
|
||||||
const currentRedoStack = redoStackRef.current
|
const currentRedoStack = redoStackRef.current
|
||||||
if (!canvas || currentRedoStack.length === 0 || _isRestoring) return
|
if (!canvas || currentRedoStack.length === 0 || _isRestoring) return
|
||||||
|
|
||||||
@ -377,11 +423,17 @@ export function useUndoRedo() {
|
|||||||
setRedoStack([])
|
setRedoStack([])
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
/** undo 스택에서 마지막 스냅샷 1개만 제거 (취소 시 사용) */
|
||||||
|
const popLastSnapshot = useCallback(() => {
|
||||||
|
setUndoStack((prev) => (prev.length > 0 ? prev.slice(0, -1) : prev))
|
||||||
|
}, [])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
saveSnapshot,
|
saveSnapshot,
|
||||||
undo,
|
undo,
|
||||||
redo,
|
redo,
|
||||||
clearStacks,
|
clearStacks,
|
||||||
|
popLastSnapshot,
|
||||||
canUndo: undoStack.length > 0,
|
canUndo: undoStack.length > 0,
|
||||||
canRedo: redoStack.length > 0,
|
canRedo: redoStack.length > 0,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user