dev #931
@ -265,6 +265,56 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
}
|
||||
})
|
||||
|
||||
// [WALL→ROOF 동기 이동]
|
||||
// 지붕형상 설정 후 wall 과 roof 는 별개의 폴리곤이고, hip/ridge/baseLine 같은 보조선은
|
||||
// 모두 parentId=roof.id 로 묶여 있어 wall 의 directChildren 에 포함되지 않는다.
|
||||
// wall 만 단독 이동시키면 roof+보조선이 옛 위치에 남아 시각적으로 어긋나므로
|
||||
// wall 이동 시 연결된 roof 를 찾아 polygonMoved 로 같은 delta 만큼 이동시킨다.
|
||||
// (polygonMoved 가 자체적으로 직속 자식 평행이동까지 처리하므로 별도 순회 불필요)
|
||||
if (this.name === POLYGON_TYPE.WALL) {
|
||||
const linkedRoof = this.canvas.getObjects().find(
|
||||
(obj) => obj.name === POLYGON_TYPE.ROOF && obj.wall === this,
|
||||
)
|
||||
if (linkedRoof) {
|
||||
linkedRoof._preDragLeft = linkedRoof.left
|
||||
linkedRoof._preDragTop = linkedRoof.top
|
||||
linkedRoof.set({
|
||||
left: (linkedRoof.left ?? 0) + dxModified,
|
||||
top: (linkedRoof.top ?? 0) + dyModified,
|
||||
})
|
||||
linkedRoof.fire('polygonMoved')
|
||||
// polygonMoved 가 ROOF 분기에서 _preDragLeft 를 정리하지 않으므로 여기서 정리
|
||||
linkedRoof._preDragLeft = null
|
||||
linkedRoof._preDragTop = null
|
||||
|
||||
// roof 의 attributes.roofId 매칭 객체(예: 일부 baseLine, 텍스트류)는
|
||||
// roof.id 와 다른 식별자로 묶여 polygonMoved 의 parentId === roof.id 필터에
|
||||
// 잡히지 않을 수 있다. 누락 방지를 위해 추가 평행이동.
|
||||
const extraKids = this.canvas.getObjects().filter((obj) => {
|
||||
if (obj === linkedRoof) return false
|
||||
if (obj.parentId === linkedRoof.id) return false // 이미 polygonMoved 가 처리
|
||||
return obj.attributes?.roofId === linkedRoof.id
|
||||
})
|
||||
extraKids.forEach((obj) => {
|
||||
const next = {}
|
||||
if (obj.left != null) next.left = obj.left + dxModified
|
||||
if (obj.top != null) next.top = obj.top + dyModified
|
||||
if (obj.x1 != null) next.x1 = obj.x1 + dxModified
|
||||
if (obj.y1 != null) next.y1 = obj.y1 + dyModified
|
||||
if (obj.x2 != null) next.x2 = obj.x2 + dxModified
|
||||
if (obj.y2 != null) next.y2 = obj.y2 + dyModified
|
||||
obj.set(next)
|
||||
if (obj.startPoint) obj.startPoint = { x: obj.startPoint.x + dxModified, y: obj.startPoint.y + dyModified }
|
||||
if (obj.endPoint) obj.endPoint = { x: obj.endPoint.x + dxModified, y: obj.endPoint.y + dyModified }
|
||||
if (obj.minX != null) obj.minX += dxModified
|
||||
if (obj.maxX != null) obj.maxX += dxModified
|
||||
if (obj.minY != null) obj.minY += dyModified
|
||||
if (obj.maxY != null) obj.maxY += dyModified
|
||||
obj.setCoords?.()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.canvas.renderAll()
|
||||
|
||||
// ROOF 의 경우 polygonMoved 가 같은 일을 다시 하므로, _preDragLeft 는 거기서 정리됨
|
||||
|
||||
@ -42,7 +42,7 @@ export function useRoofShapePassivitySetting(id) {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const { closePopup } = usePopup()
|
||||
const { saveSnapshot } = useUndoRedo()
|
||||
const { saveSnapshot, setDrawing } = useUndoRedo()
|
||||
const buttons = [
|
||||
{ id: 1, name: getMessage('eaves'), type: TYPES.EAVES },
|
||||
{ id: 2, name: getMessage('gable'), type: TYPES.GABLE },
|
||||
@ -56,7 +56,13 @@ export function useRoofShapePassivitySetting(id) {
|
||||
closePopup(id)
|
||||
//return
|
||||
}
|
||||
// 수동설정 popup 진행 중에는 undo/redo/snapshot 차단
|
||||
setDrawing(true)
|
||||
setIsLoading(true)
|
||||
return () => {
|
||||
// popup 닫힐 때 (handleSave 후 closePopup, 또는 사용자가 그냥 닫기) 잠금 해제
|
||||
setDrawing(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -384,10 +384,7 @@ export function useUndoRedo() {
|
||||
const saveSnapshot = useCallback((atomOverrides = null, { force = false } = {}) => {
|
||||
if (!canvas || _isRestoring) return
|
||||
|
||||
// 지붕형상 수동설정 메뉴에서는 스냅샷을 쌓지 않는다 (force 시 예외)
|
||||
if (!force && currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||
|
||||
// 외벽선/배치면 등 그리기 진행 중에는 스냅샷을 쌓지 않는다 (force 시 예외)
|
||||
// 외벽선/배치면/지붕형상 수동설정 등 그리기 진행 중에는 스냅샷을 쌓지 않는다 (force 시 예외)
|
||||
if (!force && (_isDrawing || isDrawing)) return
|
||||
|
||||
// snapshot 전에 mouseLine 제거
|
||||
@ -428,10 +425,7 @@ export function useUndoRedo() {
|
||||
}, [setPopup])
|
||||
|
||||
const undo = useCallback(() => {
|
||||
// 지붕형상 수동설정 중에는 undo 차단
|
||||
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||
|
||||
// 외벽선/배치면 등 그리기 진행 중에는 undo 차단
|
||||
// 외벽선/배치면/지붕형상 수동설정 등 그리기 진행 중에는 undo 차단
|
||||
if (_isDrawing || isDrawing) return
|
||||
|
||||
const currentUndoStack = undoStackRef.current
|
||||
@ -460,10 +454,7 @@ export function useUndoRedo() {
|
||||
}, [canvas, currentMenu, isDrawing, getCanvasSnapshot, getAtomSnapshot, restoreAtomSnapshot, rebuildCanvasAtoms, closeAllPopups])
|
||||
|
||||
const redo = useCallback(() => {
|
||||
// 지붕형상 수동설정 중에는 redo 차단
|
||||
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||
|
||||
// 외벽선/배치면 등 그리기 진행 중에는 redo 차단
|
||||
// 외벽선/배치면/지붕형상 수동설정 등 그리기 진행 중에는 redo 차단
|
||||
if (_isDrawing || isDrawing) return
|
||||
|
||||
const currentRedoStack = redoStackRef.current
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user