feat(shortcut): h/a/d
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
useObjectStore,
|
||||
} from "./store"
|
||||
import { useBottomToolsStore } from "./useBottomToolsStore"
|
||||
import { useKeyboardStore } from "./useKeyBoardStore"
|
||||
import { useOtherToolsStore } from "./useOtherToolsStore"
|
||||
import { usePaperSupportStore } from "./usePaperSupportStore"
|
||||
import { useRightToolsStore } from "./useRightToolsStore"
|
||||
@@ -2855,6 +2856,139 @@ export const usePaperStore = create<PaperState>((set) => ({
|
||||
})
|
||||
}
|
||||
|
||||
// paper path data里可能带函数(例如事件回调), 不能直接structuredClone
|
||||
const getSafePathData = (data: any) => {
|
||||
const nextData: Record<string, any> = {}
|
||||
Object.entries(data || {}).forEach(([key, value]) => {
|
||||
if (typeof value === "function") return
|
||||
nextData[key] = value
|
||||
})
|
||||
return nextData
|
||||
}
|
||||
|
||||
const handleSelectedPolygonSubtract = (draftPath: paper.Path) => {
|
||||
const activeImage = useBottomToolsStore.getState().activeImage
|
||||
const selectedIds =
|
||||
useObjectStore.getState().selectedPath[activeImage] || []
|
||||
if (selectedIds.length !== 1) return false
|
||||
|
||||
const selectedId = selectedIds[0]
|
||||
const targetShapes = usePaperStore
|
||||
.getState()
|
||||
.getItemsById(selectedId)
|
||||
.filter((item) => item.data?.type === "polygon")
|
||||
|
||||
const targetShape =
|
||||
targetShapes.find((item) => item instanceof paper.CompoundPath) ||
|
||||
targetShapes.find(
|
||||
(item) => item instanceof paper.Path && !item.data?.isHollowPolygon
|
||||
) ||
|
||||
targetShapes.find((item) => item instanceof paper.Path)
|
||||
|
||||
if (!targetShape) return false
|
||||
|
||||
let clippedDraft: paper.Path | paper.CompoundPath | null =
|
||||
draftPath.intersect(usePaperStore.getState().rasterPath!, {
|
||||
insert: false,
|
||||
trace: false,
|
||||
}) as paper.Path | paper.CompoundPath
|
||||
|
||||
draftPath.remove()
|
||||
|
||||
const draftHasSegments =
|
||||
clippedDraft instanceof paper.CompoundPath
|
||||
? (clippedDraft.children as paper.Path[]).some(
|
||||
(child) => child.segments?.length
|
||||
)
|
||||
: clippedDraft.segments?.length
|
||||
if (!draftHasSegments) {
|
||||
clippedDraft?.remove()
|
||||
clippedDraft = null
|
||||
return false
|
||||
}
|
||||
|
||||
const targetData = getSafePathData(targetShape.data || {})
|
||||
let nextPolygon = (
|
||||
targetShape as paper.Path | paper.CompoundPath
|
||||
).subtract(clippedDraft) as paper.Path | paper.CompoundPath
|
||||
clippedDraft?.remove()
|
||||
clippedDraft = null
|
||||
targetShape.remove()
|
||||
|
||||
if (nextPolygon instanceof paper.CompoundPath) {
|
||||
nextPolygon.data = Object.assign({}, targetData)
|
||||
;(nextPolygon.children as paper.Path[]).forEach((child) => {
|
||||
child.data = Object.assign({}, targetData, {
|
||||
isHollowPolygon: !child.clockwise,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
nextPolygon.data = Object.assign({}, targetData)
|
||||
}
|
||||
|
||||
const points: any[] = []
|
||||
const hollowPoints: any[] = []
|
||||
const collectPathPoints = (path: paper.Path) => {
|
||||
if (!path.segments?.length) return
|
||||
const pathData = JSON.parse(path.exportJSON({ precision: 20 }))
|
||||
if (path.clockwise) {
|
||||
points.push(pathData[1]?.segments)
|
||||
} else {
|
||||
hollowPoints.push(pathData[1]?.segments)
|
||||
}
|
||||
}
|
||||
|
||||
if (nextPolygon instanceof paper.CompoundPath) {
|
||||
;(nextPolygon.children as paper.Path[]).forEach((child) => {
|
||||
collectPathPoints(child)
|
||||
})
|
||||
} else {
|
||||
collectPathPoints(nextPolygon)
|
||||
}
|
||||
|
||||
const operationId = (
|
||||
targetData.operationId ||
|
||||
useTopToolsStore.getState().activeOperation ||
|
||||
""
|
||||
).toString()
|
||||
if (!operationId) {
|
||||
nextPolygon.remove()
|
||||
return false
|
||||
}
|
||||
const prevDetail = safeClone(
|
||||
useLabelStore
|
||||
.getState()
|
||||
.getLabelDetailDataByKeys(activeImage, operationId, selectedId) ||
|
||||
initialDetail
|
||||
)
|
||||
|
||||
if (points.length) {
|
||||
useLabelStore
|
||||
.getState()
|
||||
.setOperation(activeImage, operationId, [
|
||||
selectedId,
|
||||
points,
|
||||
prevDetail,
|
||||
hollowPoints,
|
||||
])
|
||||
useObjectStore
|
||||
.getState()
|
||||
.updateSelectedPath(activeImage, "ADD", selectedId)
|
||||
} else {
|
||||
nextPolygon.remove()
|
||||
useLabelStore
|
||||
.getState()
|
||||
.deleteOperation(activeImage, operationId, selectedId)
|
||||
useObjectStore.getState().updateSelectedPath(activeImage, "ADD")
|
||||
const nextLabel = safeClone(useLabelStore.getState().label)
|
||||
useLabelStore.getState().setLabel(nextLabel)
|
||||
useLabelStore.getState().pushStateStack(nextLabel)
|
||||
}
|
||||
|
||||
forceUpdate()
|
||||
return true
|
||||
}
|
||||
|
||||
polygonTool.onMouseDown = (e: {
|
||||
event: MouseEvent
|
||||
point: paper.Point
|
||||
@@ -2897,35 +3031,83 @@ export const usePaperStore = create<PaperState>((set) => ({
|
||||
// 初始化 compoundPolygon
|
||||
compoundPolygon = new paper.Path()
|
||||
|
||||
if (useKeyboardStore.getState().drawAction === "subtract") {
|
||||
;(polygon as paper.Path).closePath()
|
||||
handleSelectedPolygonSubtract(polygon as paper.Path)
|
||||
|
||||
selectedCircles.forEach((item) => {
|
||||
item.remove()
|
||||
})
|
||||
selectedCircles = []
|
||||
clearPolygonMagnet()
|
||||
useTopToolsStore.getState().setPressA(false)
|
||||
draftId = null
|
||||
lastPoint = null
|
||||
lastTime = 0
|
||||
points = []
|
||||
polygon = null
|
||||
return
|
||||
}
|
||||
|
||||
// pressA 模式下添加多边形
|
||||
if (useTopToolsStore.getState().pressA) {
|
||||
let ids =
|
||||
useObjectStore.getState().selectedPath[
|
||||
useBottomToolsStore.getState().activeImage
|
||||
]
|
||||
const selectedId = ids?.[0]
|
||||
if (!selectedId) {
|
||||
resetPolygonDraft()
|
||||
return
|
||||
}
|
||||
let paths = usePaperStore.getState().getItemsById(ids[0])
|
||||
let oldPolygon = polygon
|
||||
// 闭合绘制路径
|
||||
oldPolygon.closePath()
|
||||
const polygonPaths = paths.filter(
|
||||
(path) => path.data?.type === "polygon"
|
||||
)
|
||||
const prevCompoundPath =
|
||||
(polygonPaths.find(
|
||||
(path) => path instanceof paper.CompoundPath
|
||||
) as paper.CompoundPath | undefined) ||
|
||||
((polygonPaths.find(
|
||||
(path) => path.parent instanceof paper.CompoundPath
|
||||
)?.parent as paper.CompoundPath | undefined) ??
|
||||
undefined)
|
||||
|
||||
if (paths.length === 1) {
|
||||
const prevPath = paths[0]
|
||||
const basePathData = getSafePathData(
|
||||
polygonPaths.find((path) => !!path.data)?.data || {}
|
||||
)
|
||||
oldPolygon.data = Object.assign({}, basePathData, oldPolygon.data, {
|
||||
id: selectedId,
|
||||
type: "polygon",
|
||||
isHollowPolygon: !oldPolygon.clockwise,
|
||||
})
|
||||
|
||||
if (prevCompoundPath) {
|
||||
// 已是多区域对象, 直接追加 child
|
||||
prevCompoundPath.addChild(oldPolygon)
|
||||
prevCompoundPath.data = Object.assign(
|
||||
{},
|
||||
getSafePathData(prevCompoundPath.data || {}),
|
||||
basePathData,
|
||||
{ id: selectedId, type: "polygon" }
|
||||
)
|
||||
polygon = prevCompoundPath
|
||||
} else {
|
||||
const prevPaths = polygonPaths.filter(
|
||||
(path): path is paper.Path => path instanceof paper.Path
|
||||
)
|
||||
polygon = usePaperStore.getState().addCompoundPath(
|
||||
renderCompoundPath,
|
||||
[prevPath, oldPolygon] as paper.Path[],
|
||||
[...prevPaths, oldPolygon] as paper.Path[],
|
||||
{
|
||||
// option
|
||||
...usePaperStore.getState().toolOption,
|
||||
},
|
||||
{ ...oldPolygon.data, id: prevPath?.data.id }
|
||||
{ ...basePathData, ...oldPolygon.data, id: selectedId }
|
||||
)
|
||||
} else if (paths.length > 1) {
|
||||
const prevPath = paths.filter(
|
||||
(path) => path instanceof paper.CompoundPath
|
||||
)[0]
|
||||
// 直接将当前路径添加到原有父路径
|
||||
prevPath.addChild(oldPolygon)
|
||||
polygon = prevPath
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user