feat(shortcut): ,/

This commit is contained in:
zhangheng
2026-04-03 18:06:52 +08:00
parent bb727d52be
commit e4aae0a7af
2 changed files with 551 additions and 26 deletions

View File

@@ -2866,6 +2866,96 @@ export const usePaperStore = create<PaperState>((set) => ({
return nextData
}
const getSegmentPoint = (segment: any): [number, number] | null => {
if (
Array.isArray(segment) &&
segment.length >= 2 &&
typeof segment[0] === "number" &&
typeof segment[1] === "number"
) {
return [segment[0], segment[1]]
}
if (
Array.isArray(segment) &&
Array.isArray(segment[0]) &&
segment[0].length >= 2 &&
typeof segment[0][0] === "number" &&
typeof segment[0][1] === "number"
) {
return [segment[0][0], segment[0][1]]
}
if (
segment?.point &&
typeof segment.point.x === "number" &&
typeof segment.point.y === "number"
) {
return [segment.point.x, segment.point.y]
}
if (
segment?.point &&
Array.isArray(segment.point) &&
segment.point.length >= 2 &&
typeof segment.point[0] === "number" &&
typeof segment.point[1] === "number"
) {
return [segment.point[0], segment.point[1]]
}
return null
}
const normalizePolygonFromSegments = (segments: any[]) => {
const polygon = (segments || [])
.map((segment) => getSegmentPoint(segment))
.filter((point): point is [number, number] => !!point)
if (polygon.length > 1) {
const [firstX, firstY] = polygon[0]
const [lastX, lastY] = polygon[polygon.length - 1]
if (
Math.abs(firstX - lastX) < 0.000001 &&
Math.abs(firstY - lastY) < 0.000001
) {
polygon.pop()
}
}
return polygon
}
const isPointInPolygon = (
point: [number, number],
polygon: [number, number][]
) => {
if (polygon.length < 3) return false
const [x, y] = point
let inside = false
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const [xi, yi] = polygon[i]
const [xj, yj] = polygon[j]
const dy = yj - yi
if (yi > y !== yj > y) {
const crossX = ((xj - xi) * (y - yi)) / (dy || 0.000001) + xi
if (x < crossX) inside = !inside
}
}
return inside
}
const getHollowFlagsByContainment = (allSegments: any[][]) => {
const polygons = allSegments.map((segments) =>
normalizePolygonFromSegments(segments)
)
return polygons.map((polygon, index) => {
if (polygon.length < 3) return false
const samplePoint = polygon[0]
let containDepth = 0
polygons.forEach((otherPolygon, otherIndex) => {
if (otherIndex === index || otherPolygon.length < 3) return
if (isPointInPolygon(samplePoint, otherPolygon)) containDepth += 1
})
return containDepth % 2 === 1
})
}
const handleSelectedPolygonSubtract = (draftPath: paper.Path) => {
const activeImage = useBottomToolsStore.getState().activeImage
const selectedIds =
@@ -2915,36 +3005,37 @@ export const usePaperStore = create<PaperState>((set) => ({
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 nextPaths =
nextPolygon instanceof paper.CompoundPath
? (nextPolygon.children as paper.Path[])
: ([nextPolygon] as paper.Path[])
const nextPathSegments = nextPaths.map((path) => {
if (!path.segments?.length) return null
const pathData = JSON.parse(path.exportJSON({ precision: 20 }))
if (path.clockwise) {
points.push(pathData[1]?.segments)
} else {
hollowPoints.push(pathData[1]?.segments)
}
}
return pathData?.[1]?.segments || null
})
const hollowFlags = getHollowFlagsByContainment(
nextPathSegments.map((segments) => segments || [])
)
if (nextPolygon instanceof paper.CompoundPath) {
;(nextPolygon.children as paper.Path[]).forEach((child) => {
collectPathPoints(child)
})
} else {
collectPathPoints(nextPolygon)
nextPolygon.data = Object.assign({}, targetData)
}
nextPaths.forEach((path, index) => {
const segments = nextPathSegments[index]
if (!segments?.length) return
const isHollowPolygon = !!hollowFlags[index]
path.data = Object.assign({}, targetData, {
isHollowPolygon,
})
if (isHollowPolygon) {
hollowPoints.push(segments)
} else {
points.push(segments)
}
})
const operationId = (
targetData.operationId ||