feat(shortcut): h/a/d

This commit is contained in:
zhangheng
2026-04-03 17:00:09 +08:00
parent b6ae2a6a29
commit bb727d52be
7 changed files with 449 additions and 83 deletions

View File

@@ -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
}
}