feat(circle): change size

This commit is contained in:
zhangheng
2026-04-15 11:04:42 +08:00
parent d3d9e8385b
commit 72b1dacf1c
4 changed files with 133 additions and 39 deletions

View File

@@ -1022,16 +1022,12 @@ const PaperContainer = (
parent: usePaperStore.getState().group,
strokeScaling: false,
})
if (Math.abs(path.area) < useTopToolsStore.getState().checkSize) {
path.remove()
if (hollows[index]) {
path.data.isHollowPolygon = true
innerPaths.push(path)
} else {
if (hollows[index]) {
path.data.isHollowPolygon = true
innerPaths.push(path)
} else {
path.fillColor = new paper.Color(fillColor)
outerPaths.push(path)
}
path.fillColor = new paper.Color(fillColor)
outerPaths.push(path)
}
}
})

View File

@@ -92,7 +92,12 @@ import { useKeyboardStore } from "../useKeyBoardStore"
import { usePaperStore } from "../usePaperStore"
import { useRightToolsStore } from "../useRightToolsStore"
import { useIntervalStore, useLabelTimeStore } from "../useTimerStore"
import { useTopToolsStore } from "../useTopToolsStore"
import {
DEFAULT_NODE_SIZE,
NODE_SIZE_MAX,
NODE_SIZE_MIN,
useTopToolsStore,
} from "../useTopToolsStore"
import { getSubAttrStatus } from "../util"
import { safeClone } from "../utils/clone"
import { labelTypeMap, TaskStatusEnum } from "../utils/constants"
@@ -222,13 +227,14 @@ const TopTools = (
setAssistToolEnabled,
assistToolSize,
setAssistToolSize,
nodeSize,
setNodeSize,
needBackup,
setNeedBackup,
checkSize,
setCheckSize,
magnetFlag,
setMagnetFlag,
} = useTopToolsStore()
const [nodeSizeInput, setNodeSizeInput] = useState<number | string>(nodeSize)
const { pathGroupMap } = useRightToolsStore()
const isAutoSave = useIntervalStore((state) => state.isAutoSave)
@@ -247,6 +253,11 @@ const TopTools = (
].filter(Boolean).length
}, [projectDetail, showDescList, showGroupList, showObjectList, showTaskList])
useEffect(() => {
setNodeSizeInput(nodeSize)
usePaperStore.getState().refreshNodeVisualSize()
}, [nodeSize])
const toggleRightPanel = useCallback(
(visible: boolean, setter: (val: boolean) => void) => {
const nextVisible = !visible
@@ -2188,23 +2199,39 @@ const TopTools = (
</Flex>
</Flex>
<Flex justify="space-between" align="center" px="xs" gap="xs">
<NumberInput
value={checkSize}
w={50}
min={1}
size="xs"
radius="sm"
hideControls
onChange={(v) => {
if (v) setCheckSize(Number(v))
}}
onFocus={() => {
useKeyEventStore.getState().setFocusInput(true)
}}
onBlur={() => {
useKeyEventStore.getState().setFocusInput(false)
}}
/>
<Tooltip label={`节点直径范围 ${NODE_SIZE_MIN}-${NODE_SIZE_MAX}`}>
<Flex align="center" gap={4}>
<Text span size="xs" style={{ minWidth: "fit-content" }}>
</Text>
<NumberInput
value={nodeSizeInput}
w={50}
min={NODE_SIZE_MIN}
max={NODE_SIZE_MAX}
step={1}
allowDecimal={false}
size="xs"
radius="sm"
hideControls
onChange={(value) => {
setNodeSizeInput(value)
}}
onFocus={() => {
useKeyEventStore.getState().setFocusInput(true)
}}
onBlur={(event) => {
useKeyEventStore.getState().setFocusInput(false)
const rawValue = event.currentTarget.value.trim()
const nextValue =
rawValue === "" ? DEFAULT_NODE_SIZE : Number(rawValue)
setNodeSize(
Number.isFinite(nextValue) ? nextValue : DEFAULT_NODE_SIZE
)
}}
/>
</Flex>
</Tooltip>
{renderEditIcon}
<Button
w="fit-content"

View File

@@ -18,7 +18,7 @@ import { useKeyboardStore } from "./useKeyBoardStore"
import { useOtherToolsStore } from "./useOtherToolsStore"
import { usePaperSupportStore } from "./usePaperSupportStore"
import { useRightToolsStore } from "./useRightToolsStore"
import { useTopToolsStore } from "./useTopToolsStore"
import { DEFAULT_NODE_SIZE, useTopToolsStore } from "./useTopToolsStore"
import { safeClone } from "./utils/clone"
interface ContinuePolygonTarget {
@@ -183,6 +183,7 @@ interface PaperState {
setContinuePolygonTarget: (target: ContinuePolygonTarget | null) => void
beginContinuePolygonDraft: () => boolean
setLoadingData: (flag: boolean) => void
refreshNodeVisualSize: () => void
resizeGhostRequestToken: number
requestResizeGhost: () => void
}
@@ -328,6 +329,7 @@ const PAPER_CONTEXT_HIT_TYPES = [
"pathBuff",
"pathCircle",
] as const
const LEGACY_PAPER_NODE_DIAMETER = 6
const isPaperObjectType = (type: unknown) =>
typeof type === "string" &&
@@ -339,6 +341,39 @@ const isPaperContextHitType = (type: unknown) =>
type as (typeof PAPER_CONTEXT_HIT_TYPES)[number]
)
const getPaperNodeDiameter = () => {
const configuredDiameter = useTopToolsStore.getState().nodeSize
return Number.isFinite(configuredDiameter)
? Math.round(configuredDiameter)
: DEFAULT_NODE_SIZE
}
const getPaperNodeRadius = () => getPaperNodeDiameter() / 2
const getPaperNodeScaleFactor = () => {
const currentScaling = usePaperStore.getState().group?.scaling.x || 1
const newScale = useTopToolsStore.getState().scale / currentScaling
return newScale / currentScaling
}
const syncPaperCircleDiameter = (circle: paper.Path) => {
const nextDiameter = getPaperNodeDiameter()
const currentDiameter =
Number(circle.data?.diameter) || LEGACY_PAPER_NODE_DIAMETER
if (
Number.isFinite(currentDiameter) &&
currentDiameter > 0 &&
Math.abs(currentDiameter - nextDiameter) > 0.001
) {
circle.scale(nextDiameter / currentDiameter)
}
circle.data = Object.assign({}, circle.data, {
diameter: nextDiameter,
})
}
const getPaperCursorByMode = (
mode: PaperState["mode"] = usePaperStore.getState().mode
) => {
@@ -529,6 +564,7 @@ const createPointCircleForPath = (
imageId: path.data.imageId,
operationId: path.data.operationId,
text: index + 1,
diameter: getPaperNodeDiameter(),
fillColor: path.data.blankColor,
strokeColor: path.data.strokeColor,
})
@@ -562,6 +598,7 @@ const syncPointCirclesWithPath = (path: paper.Path) => {
imageId: path.data.imageId,
operationId: path.data.operationId,
text: index + 1,
diameter: getPaperNodeDiameter(),
fillColor: path.data.blankColor,
strokeColor: path.data.strokeColor,
})
@@ -1014,6 +1051,19 @@ export const usePaperStore = create<PaperState>((set) => ({
...state,
loadingData: flag,
})),
refreshNodeVisualSize: () => {
const group = usePaperStore.getState().group
if (!group) return
;(
group.getItems({
match: (item: paper.Item) =>
item instanceof paper.Path &&
["circle", "pathCircle"].includes(item.data?.type || ""),
}) as paper.Path[]
).forEach((circle) => {
syncPaperCircleDiameter(circle)
})
},
requestResizeGhost: () =>
set((state: PaperState) => ({
...state,
@@ -5427,6 +5477,7 @@ export const usePaperStore = create<PaperState>((set) => ({
const newScale = useTopToolsStore.getState().scale / currentScaling
myCircle.scale(newScale / currentScaling)
myCircle.data = Object.assign({ type: "circle", ...option }, data)
syncPaperCircleDiameter(myCircle)
myCircle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
if (e.event && e.event.button === 2) {
if (
@@ -5844,6 +5895,7 @@ export const usePaperStore = create<PaperState>((set) => ({
id: number,
color: any
) => {
const nodeDiameter = getPaperNodeDiameter()
let circle: paper.Path.Circle = new paper.Path.Circle(
Object.assign(
{ parent: usePaperStore.getState().group!, center: point },
@@ -5851,12 +5903,13 @@ export const usePaperStore = create<PaperState>((set) => ({
fillColor: color,
// center: point,
strokeColor: usePaperStore.getState().toolOption?.strokeColor,
radius: 3,
radius: getPaperNodeRadius(),
strokeScaling: false,
},
{
data: {
type: "circle",
diameter: nodeDiameter,
fillColor: color,
strokeColor: usePaperStore.getState().toolOption?.strokeColor,
imageId: useBottomToolsStore.getState().activeImage,
@@ -5868,9 +5921,7 @@ export const usePaperStore = create<PaperState>((set) => ({
}
)
)
const currentScaling = usePaperStore.getState().group!.scaling.x
const newScale = useTopToolsStore.getState().scale / currentScaling
circle.scale(newScale / currentScaling)
circle.scale(getPaperNodeScaleFactor())
circle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
if (e.event && e.event.button === 2) {
if (
@@ -6051,8 +6102,7 @@ export const usePaperStore = create<PaperState>((set) => ({
id: number,
color: any
) => {
const currentScaling = usePaperStore.getState().group!.scaling.x
const newScale = useTopToolsStore.getState().scale / currentScaling
const nodeDiameter = getPaperNodeDiameter()
let circle: paper.Path.Circle = new paper.Path.Circle(
Object.assign(
{ parent: usePaperStore.getState().group!, center: point },
@@ -6060,15 +6110,16 @@ export const usePaperStore = create<PaperState>((set) => ({
fillColor: color,
// center: point,
strokeColor: "#FFF",
radius: 3,
strokeWidth: 2,
radius: getPaperNodeRadius(),
strokeScaling: false,
},
{
data: { index, type: "pathCircle", id },
data: { index, type: "pathCircle", id, diameter: nodeDiameter },
}
)
)
circle.scale(newScale / currentScaling)
circle.scale(getPaperNodeScaleFactor())
circle.onMouseEnter = (_e: any) => {
hoveredPathCircleInfo = {
id,

View File

@@ -6,6 +6,17 @@ import { useObjectStore } from "./store"
import { useBottomToolsStore } from "./useBottomToolsStore"
import { safeClone } from "./utils/clone"
export const NODE_SIZE_MIN = 6
export const NODE_SIZE_MAX = 24
export const DEFAULT_NODE_SIZE = 10
const clampNodeSize = (value: number) => {
const nextValue = Number.isFinite(value)
? Math.round(value)
: DEFAULT_NODE_SIZE
return Math.min(NODE_SIZE_MAX, Math.max(NODE_SIZE_MIN, nextValue))
}
interface TopToolsState {
// 标注页面是否仅查看
isView: boolean
@@ -34,6 +45,8 @@ interface TopToolsState {
setAssistToolEnabled: (val: boolean) => void
assistToolSize: number
setAssistToolSize: (val: number) => void
nodeSize: number
setNodeSize: (val: number) => void
showTags: boolean
setShowTags: (val: boolean) => void
showTagsConfigs: string[]
@@ -94,6 +107,7 @@ const initialTopToolsState = {
crosshairStatus: "hidden" as "hidden" | "line" | "carve",
assistToolEnabled: false,
assistToolSize: 10,
nodeSize: DEFAULT_NODE_SIZE,
drawOption: "default" as "default" | "intersect" | "unite",
saveCurrentScale: false,
scale: 1,
@@ -183,6 +197,12 @@ export const useTopToolsStore = create<TopToolsState>()(
...state,
assistToolSize: Math.max(1, val),
})),
nodeSize: initialTopToolsState.nodeSize,
setNodeSize: (val) =>
set((state: TopToolsState) => ({
...state,
nodeSize: clampNodeSize(val),
})),
showTags: false,
setShowTags: (val) =>
set((state: TopToolsState) => ({