feat(tool): shape
This commit is contained in:
77
components/label/components/AssistShapeComponent.tsx
Normal file
77
components/label/components/AssistShapeComponent.tsx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
"use client"
|
||||||
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
} from "react"
|
||||||
|
|
||||||
|
interface AssistShapeComponentProps {
|
||||||
|
size: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssistShapeComponent = (
|
||||||
|
props: AssistShapeComponentProps,
|
||||||
|
ref: React.Ref<unknown> | undefined
|
||||||
|
) => {
|
||||||
|
const { size } = props
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||||
|
|
||||||
|
const drawAssistShape = useCallback(
|
||||||
|
(centerX: number, centerY: number) => {
|
||||||
|
const canvas = canvasRef.current
|
||||||
|
const ctx = canvas?.getContext("2d")
|
||||||
|
if (!canvas || !ctx) return
|
||||||
|
|
||||||
|
const radius = Math.max(1, size)
|
||||||
|
const side = radius * 2
|
||||||
|
const left = centerX - radius
|
||||||
|
const top = centerY - radius
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.strokeStyle = "#EF4444"
|
||||||
|
ctx.lineWidth = 2
|
||||||
|
ctx.setLineDash([6, 4])
|
||||||
|
ctx.strokeRect(left, top, side, side)
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2)
|
||||||
|
ctx.stroke()
|
||||||
|
},
|
||||||
|
[size]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current
|
||||||
|
if (!canvas) return
|
||||||
|
canvas.width = window.innerWidth
|
||||||
|
canvas.height = window.innerHeight
|
||||||
|
const rect = canvas.getBoundingClientRect()
|
||||||
|
drawAssistShape(rect.width / 2, rect.height / 2)
|
||||||
|
}, [drawAssistShape])
|
||||||
|
|
||||||
|
const updateAssistShape = (event: { clientX: number; clientY: number }) => {
|
||||||
|
const canvas = canvasRef.current
|
||||||
|
if (!canvas) return
|
||||||
|
const rect = canvas.getBoundingClientRect()
|
||||||
|
const centerX = event.clientX - rect.left
|
||||||
|
const centerY = event.clientY - rect.top
|
||||||
|
drawAssistShape(centerX, centerY)
|
||||||
|
}
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({ updateAssistShape }))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<canvas
|
||||||
|
ref={canvasRef}
|
||||||
|
style={{
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: 52,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default forwardRef(AssistShapeComponent)
|
||||||
@@ -59,6 +59,7 @@ import { checkCommentsIsSame } from "../util"
|
|||||||
import { safeClone } from "../utils/clone"
|
import { safeClone } from "../utils/clone"
|
||||||
import { labelTypeMap } from "../utils/constants"
|
import { labelTypeMap } from "../utils/constants"
|
||||||
import { adjustPoints } from "../utils/paperjs"
|
import { adjustPoints } from "../utils/paperjs"
|
||||||
|
import AssistShapeComponent from "./AssistShapeComponent"
|
||||||
import CrosshairComponent from "./CrosshairComponent"
|
import CrosshairComponent from "./CrosshairComponent"
|
||||||
import { renderOperationIcon } from "./RightObjectTools"
|
import { renderOperationIcon } from "./RightObjectTools"
|
||||||
|
|
||||||
@@ -222,6 +223,8 @@ const PaperContainer = (
|
|||||||
objectOperations,
|
objectOperations,
|
||||||
imageFilter,
|
imageFilter,
|
||||||
crosshairStatus,
|
crosshairStatus,
|
||||||
|
assistToolEnabled,
|
||||||
|
assistToolSize,
|
||||||
showTags,
|
showTags,
|
||||||
showTagsConfigs,
|
showTagsConfigs,
|
||||||
} = useTopToolsStore()
|
} = useTopToolsStore()
|
||||||
@@ -2671,9 +2674,11 @@ const PaperContainer = (
|
|||||||
}, [activeImage])
|
}, [activeImage])
|
||||||
|
|
||||||
const crosshairComponentRef = useRef<any>(null)
|
const crosshairComponentRef = useRef<any>(null)
|
||||||
|
const assistShapeComponentRef = useRef<any>(null)
|
||||||
|
|
||||||
const handleCrosshairMove = (event: { clientX: number; clientY: number }) => {
|
const handleCrosshairMove = (event: { clientX: number; clientY: number }) => {
|
||||||
crosshairComponentRef.current?.updateCrosshair(event)
|
crosshairComponentRef.current?.updateCrosshair(event)
|
||||||
|
assistShapeComponentRef.current?.updateAssistShape(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
@@ -2703,6 +2708,12 @@ const PaperContainer = (
|
|||||||
ref={crosshairComponentRef}
|
ref={crosshairComponentRef}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{assistToolEnabled && (
|
||||||
|
<AssistShapeComponent
|
||||||
|
size={assistToolSize}
|
||||||
|
ref={assistShapeComponentRef}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{imgSize?.clientWidth && imgSize?.clientHeight && (
|
{imgSize?.clientWidth && imgSize?.clientHeight && (
|
||||||
<canvas
|
<canvas
|
||||||
id="paper-canvas"
|
id="paper-canvas"
|
||||||
|
|||||||
@@ -214,6 +214,10 @@ const TopTools = (
|
|||||||
setImageFilter,
|
setImageFilter,
|
||||||
crosshairStatus,
|
crosshairStatus,
|
||||||
setCrosshairStatus,
|
setCrosshairStatus,
|
||||||
|
assistToolEnabled,
|
||||||
|
setAssistToolEnabled,
|
||||||
|
assistToolSize,
|
||||||
|
setAssistToolSize,
|
||||||
needBackup,
|
needBackup,
|
||||||
setNeedBackup,
|
setNeedBackup,
|
||||||
checkSize,
|
checkSize,
|
||||||
@@ -2030,6 +2034,36 @@ const TopTools = (
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Flex justify="space-between" align="center" p={8} gap={4}>
|
||||||
|
<ActionIcon
|
||||||
|
variant="transparent"
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
c={assistToolEnabled ? "blue" : "var(--mantine-color-text)"}
|
||||||
|
onClick={() => {
|
||||||
|
setAssistToolEnabled(!assistToolEnabled)
|
||||||
|
}}>
|
||||||
|
<Eclipse size={16} />
|
||||||
|
</ActionIcon>
|
||||||
|
<NumberInput
|
||||||
|
value={assistToolSize}
|
||||||
|
w={58}
|
||||||
|
min={1}
|
||||||
|
size="xs"
|
||||||
|
hideControls
|
||||||
|
onChange={(v) => {
|
||||||
|
const next = Number(v)
|
||||||
|
if (Number.isFinite(next) && next >= 1) {
|
||||||
|
setAssistToolSize(next)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFocus={() => {
|
||||||
|
useKeyEventStore.getState().setFocusInput(true)
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
useKeyEventStore.getState().setFocusInput(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
<Flex justify="center" align="center">
|
<Flex justify="center" align="center">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ interface TopToolsState {
|
|||||||
) => void
|
) => void
|
||||||
crosshairStatus: "hidden" | "line" | "carve"
|
crosshairStatus: "hidden" | "line" | "carve"
|
||||||
setCrosshairStatus: (val: "hidden" | "line" | "carve") => void
|
setCrosshairStatus: (val: "hidden" | "line" | "carve") => void
|
||||||
|
assistToolEnabled: boolean
|
||||||
|
setAssistToolEnabled: (val: boolean) => void
|
||||||
|
assistToolSize: number
|
||||||
|
setAssistToolSize: (val: number) => void
|
||||||
showTags: boolean
|
showTags: boolean
|
||||||
setShowTags: (val: boolean) => void
|
setShowTags: (val: boolean) => void
|
||||||
showTagsConfigs: string[]
|
showTagsConfigs: string[]
|
||||||
@@ -85,6 +89,8 @@ const initialTopToolsState = {
|
|||||||
brightness: 0,
|
brightness: 0,
|
||||||
},
|
},
|
||||||
crosshairStatus: "hidden" as "hidden" | "line" | "carve",
|
crosshairStatus: "hidden" as "hidden" | "line" | "carve",
|
||||||
|
assistToolEnabled: false,
|
||||||
|
assistToolSize: 10,
|
||||||
drawOption: "default" as "default" | "intersect" | "unite",
|
drawOption: "default" as "default" | "intersect" | "unite",
|
||||||
saveCurrentScale: false,
|
saveCurrentScale: false,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
@@ -156,6 +162,18 @@ export const useTopToolsStore = create<TopToolsState>()(
|
|||||||
...state,
|
...state,
|
||||||
crosshairStatus: val,
|
crosshairStatus: val,
|
||||||
})),
|
})),
|
||||||
|
assistToolEnabled: initialTopToolsState.assistToolEnabled,
|
||||||
|
setAssistToolEnabled: (val) =>
|
||||||
|
set((state: TopToolsState) => ({
|
||||||
|
...state,
|
||||||
|
assistToolEnabled: val,
|
||||||
|
})),
|
||||||
|
assistToolSize: initialTopToolsState.assistToolSize,
|
||||||
|
setAssistToolSize: (val) =>
|
||||||
|
set((state: TopToolsState) => ({
|
||||||
|
...state,
|
||||||
|
assistToolSize: Math.max(1, val),
|
||||||
|
})),
|
||||||
showTags: false,
|
showTags: false,
|
||||||
setShowTags: (val) =>
|
setShowTags: (val) =>
|
||||||
set((state: TopToolsState) => ({
|
set((state: TopToolsState) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user