fix(bug): fix
This commit is contained in:
@@ -33,6 +33,8 @@ import {
|
|||||||
IconRefresh,
|
IconRefresh,
|
||||||
IconSearch,
|
IconSearch,
|
||||||
} from "@tabler/icons-react"
|
} from "@tabler/icons-react"
|
||||||
|
import dayjs from "dayjs"
|
||||||
|
import duration from "dayjs/plugin/duration"
|
||||||
import { DataTable, DataTableColumn } from "mantine-datatable"
|
import { DataTable, DataTableColumn } from "mantine-datatable"
|
||||||
import { useRouter, useSearchParams } from "next/navigation"
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
@@ -41,6 +43,7 @@ import DispatchModal from "./components/DispatchModal"
|
|||||||
import ReleaseModal from "./components/ReleaseModal"
|
import ReleaseModal from "./components/ReleaseModal"
|
||||||
import TaskStatusTag from "./components/TaskStatusTag"
|
import TaskStatusTag from "./components/TaskStatusTag"
|
||||||
import WorkflowModal from "./components/WorkflowModal"
|
import WorkflowModal from "./components/WorkflowModal"
|
||||||
|
dayjs.extend(duration)
|
||||||
|
|
||||||
interface FilterFormRecord {
|
interface FilterFormRecord {
|
||||||
search_id: string
|
search_id: string
|
||||||
@@ -441,6 +444,10 @@ export default function TaskTableContainer(props: {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getTime = (time: number) => {
|
||||||
|
return dayjs.duration(time, "seconds").format("HH:mm:ss")
|
||||||
|
}
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const cols: DataTableColumn<Task.DataProps>[] = [
|
const cols: DataTableColumn<Task.DataProps>[] = [
|
||||||
{
|
{
|
||||||
@@ -673,37 +680,55 @@ export default function TaskTableContainer(props: {
|
|||||||
accessor: "work_time.label_work_time",
|
accessor: "work_time.label_work_time",
|
||||||
title: "标注总耗时",
|
title: "标注总耗时",
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (record) => record.work_time?.label_work_time ?? "-",
|
render: (record) =>
|
||||||
|
record.work_time?.label_work_time
|
||||||
|
? getTime(record.work_time?.label_work_time)
|
||||||
|
: "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "work_time.first_review_work_time",
|
accessor: "work_time.first_review_work_time",
|
||||||
title: "审核总耗时",
|
title: "审核总耗时",
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (record) => record.work_time?.first_review_work_time ?? "-",
|
render: (record) =>
|
||||||
|
record.work_time?.first_review_work_time
|
||||||
|
? getTime(record.work_time?.first_review_work_time)
|
||||||
|
: "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "work_time.second_review_work_time",
|
accessor: "work_time.second_review_work_time",
|
||||||
title: "复审总耗时",
|
title: "复审总耗时",
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (record) => record.work_time?.second_review_work_time ?? "-",
|
render: (record) =>
|
||||||
|
record.work_time?.second_review_work_time
|
||||||
|
? getTime(record.work_time?.second_review_work_time)
|
||||||
|
: "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "label_first_work_time",
|
accessor: "label_first_work_time",
|
||||||
title: "标注首次耗时",
|
title: "标注首次耗时",
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (record) => record.label_first_work_time ?? "-",
|
render: (record) =>
|
||||||
|
record.label_first_work_time
|
||||||
|
? getTime(record.label_first_work_time)
|
||||||
|
: "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "review1_first_work_time",
|
accessor: "review1_first_work_time",
|
||||||
title: "审核首次耗时",
|
title: "审核首次耗时",
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (record) => record.review1_first_work_time ?? "-",
|
render: (record) =>
|
||||||
|
record.review1_first_work_time
|
||||||
|
? getTime(record.review1_first_work_time)
|
||||||
|
: "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "review2_first_work_time",
|
accessor: "review2_first_work_time",
|
||||||
title: "复审首次耗时",
|
title: "复审首次耗时",
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (record) => record.review2_first_work_time ?? "-",
|
render: (record) =>
|
||||||
|
record.review2_first_work_time
|
||||||
|
? getTime(record.review2_first_work_time)
|
||||||
|
: "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "first_reject_label_time",
|
accessor: "first_reject_label_time",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const DISPLAY_COLUMNS: Array<{ accessor: string; title: string }> = [
|
|||||||
{ accessor: "task_status_dst", title: "目标状态" },
|
{ accessor: "task_status_dst", title: "目标状态" },
|
||||||
{ accessor: "old_username", title: "原处理人" },
|
{ accessor: "old_username", title: "原处理人" },
|
||||||
{ accessor: "new_username", title: "目标处理人" },
|
{ accessor: "new_username", title: "目标处理人" },
|
||||||
|
{ accessor: "create_at", title: "创建时间" },
|
||||||
{ accessor: "action", title: "操作" },
|
{ accessor: "action", title: "操作" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ import {
|
|||||||
IconChevronsLeft,
|
IconChevronsLeft,
|
||||||
IconChevronsRight,
|
IconChevronsRight,
|
||||||
IconFlag,
|
IconFlag,
|
||||||
|
IconPlayerTrackNext,
|
||||||
|
IconPlayerTrackNextFilled,
|
||||||
|
IconPlayerTrackPrev,
|
||||||
|
IconPlayerTrackPrevFilled,
|
||||||
} from "@tabler/icons-react"
|
} from "@tabler/icons-react"
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||||
import { Project } from "../api/project/typing"
|
import { Project } from "../api/project/typing"
|
||||||
@@ -46,6 +50,8 @@ interface BottomToolsComponentProps {
|
|||||||
) => void | null
|
) => void | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PlaybackDirection = "backward" | "forward"
|
||||||
|
|
||||||
const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
||||||
const { labelState, projectDetail, taskDetail, renderPolygons } = props
|
const { labelState, projectDetail, taskDetail, renderPolygons } = props
|
||||||
|
|
||||||
@@ -72,35 +78,34 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
|||||||
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [conflictOpen, setConflictOpen] = useState(false)
|
const [conflictOpen, setConflictOpen] = useState(false)
|
||||||
|
const [playDirection, setPlayDirection] = useState<PlaybackDirection | null>(
|
||||||
|
null
|
||||||
|
)
|
||||||
|
const allKeys = useMemo(() => Array.from(labelState.keys()), [labelState])
|
||||||
const isShowKeyFrame =
|
const isShowKeyFrame =
|
||||||
projectDetail &&
|
projectDetail &&
|
||||||
[5, 6].includes(projectDetail.label_type) &&
|
[5, 6].includes(projectDetail.label_type) &&
|
||||||
Array.from(labelState.keys()).some(
|
allKeys.some(
|
||||||
(k) => keyFrameData.has(k) && keyFrameData.get(k)?.key_frame
|
(k) => keyFrameData.has(k) && keyFrameData.get(k)?.key_frame
|
||||||
) &&
|
) &&
|
||||||
onlyKeyFrame
|
onlyKeyFrame
|
||||||
const currentKeys = Array.from(labelState.keys()).filter((k) =>
|
const currentKeys = useMemo(
|
||||||
isShowKeyFrame
|
() =>
|
||||||
? keyFrameData.has(k) && keyFrameData.get(k)?.key_frame
|
allKeys.filter((k) =>
|
||||||
: true
|
isShowKeyFrame
|
||||||
|
? keyFrameData.has(k) && keyFrameData.get(k)?.key_frame
|
||||||
|
: true
|
||||||
|
),
|
||||||
|
[allKeys, isShowKeyFrame, keyFrameData]
|
||||||
)
|
)
|
||||||
// 上一帧图片id
|
// 上一帧图片id
|
||||||
const previousFrameKey = useMemo(() => {
|
const previousFrameKey = useMemo(() => {
|
||||||
let key = ""
|
const currentIndex = currentKeys.findIndex((k) => k === activeImage)
|
||||||
const keys = Array.from(labelState.keys()).filter((k) =>
|
return currentIndex > 0 ? currentKeys[currentIndex - 1] : ""
|
||||||
isShowKeyFrame
|
}, [activeImage, currentKeys])
|
||||||
? keyFrameData.has(k) && keyFrameData.get(k)?.key_frame
|
|
||||||
: true
|
|
||||||
)
|
|
||||||
keys.forEach((k, index) => {
|
|
||||||
if (k === activeImage && index > 0) key = keys[index - 1]
|
|
||||||
})
|
|
||||||
return key
|
|
||||||
}, [activeImage, isShowKeyFrame, keyFrameData, labelState])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const keys = Array.from(labelState.keys())
|
if (!currentKeys.length) {
|
||||||
if (!keys.length) {
|
|
||||||
setActiveImage("")
|
setActiveImage("")
|
||||||
setInputNumber("")
|
setInputNumber("")
|
||||||
setActiveImageId(0)
|
setActiveImageId(0)
|
||||||
@@ -108,36 +113,42 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentIndex = activeImage
|
const currentIndex = activeImage
|
||||||
? keys.findIndex((k) => k === activeImage)
|
? currentKeys.findIndex((k) => k === activeImage)
|
||||||
: -1
|
: -1
|
||||||
if (currentIndex >= 0) {
|
if (currentIndex >= 0) {
|
||||||
setInputNumber(String(currentIndex + 1))
|
setInputNumber(String(currentIndex + 1))
|
||||||
setActiveImageId(currentIndex + 1)
|
const absoluteIndex = allKeys.findIndex((k) => k === activeImage)
|
||||||
|
setActiveImageId(absoluteIndex >= 0 ? absoluteIndex + 1 : 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstKey = keys[0]
|
const firstKey = currentKeys[0]
|
||||||
|
const absoluteIndex = allKeys.findIndex((k) => k === firstKey)
|
||||||
setActiveImage(firstKey)
|
setActiveImage(firstKey)
|
||||||
setInputNumber("1")
|
setInputNumber("1")
|
||||||
setActiveImageId(1)
|
setActiveImageId(absoluteIndex >= 0 ? absoluteIndex + 1 : 1)
|
||||||
}, [
|
}, [
|
||||||
activeImage,
|
activeImage,
|
||||||
labelState,
|
allKeys,
|
||||||
|
currentKeys,
|
||||||
setActiveImage,
|
setActiveImage,
|
||||||
setActiveImageId,
|
setActiveImageId,
|
||||||
setInputNumber,
|
setInputNumber,
|
||||||
])
|
])
|
||||||
|
|
||||||
const scrollToIndex = (index: number) => {
|
const scrollToIndex = useCallback(
|
||||||
const key = currentKeys[index]
|
(index: number) => {
|
||||||
if (key && itemRefs.current[key]) {
|
const key = currentKeys[index]
|
||||||
itemRefs.current[key]?.scrollIntoView({
|
if (key && itemRefs.current[key]) {
|
||||||
behavior: "smooth",
|
itemRefs.current[key]?.scrollIntoView({
|
||||||
block: "nearest",
|
behavior: "smooth",
|
||||||
inline: "center",
|
block: "nearest",
|
||||||
})
|
inline: "center",
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
[currentKeys]
|
||||||
|
)
|
||||||
|
|
||||||
const closeRightContext = useCallback(() => {
|
const closeRightContext = useCallback(() => {
|
||||||
useOtherToolsStore.getState().setShowContextMenu({
|
useOtherToolsStore.getState().setShowContextMenu({
|
||||||
@@ -152,6 +163,72 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const goToVisibleIndex = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
if (index < 0 || index >= currentKeys.length) return false
|
||||||
|
|
||||||
|
const nextImage = currentKeys[index]
|
||||||
|
const absoluteIndex = allKeys.findIndex((k) => k === nextImage)
|
||||||
|
|
||||||
|
scrollToIndex(index)
|
||||||
|
setInputNumber(String(index + 1))
|
||||||
|
setActiveImageId(absoluteIndex >= 0 ? absoluteIndex + 1 : 0)
|
||||||
|
setActiveImage(nextImage)
|
||||||
|
closeRightContext()
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
[
|
||||||
|
allKeys,
|
||||||
|
closeRightContext,
|
||||||
|
currentKeys,
|
||||||
|
scrollToIndex,
|
||||||
|
setActiveImage,
|
||||||
|
setActiveImageId,
|
||||||
|
setInputNumber,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const stepVisibleImage = useCallback(
|
||||||
|
(direction: PlaybackDirection) => {
|
||||||
|
const currentIndex = currentKeys.findIndex((k) => k === activeImage)
|
||||||
|
if (currentIndex < 0) return false
|
||||||
|
|
||||||
|
const nextIndex =
|
||||||
|
direction === "backward" ? currentIndex - 1 : currentIndex + 1
|
||||||
|
|
||||||
|
return goToVisibleIndex(nextIndex)
|
||||||
|
},
|
||||||
|
[activeImage, currentKeys, goToVisibleIndex]
|
||||||
|
)
|
||||||
|
|
||||||
|
const togglePlayback = useCallback(
|
||||||
|
(direction: PlaybackDirection) => {
|
||||||
|
if (currentKeys.length < 2) return
|
||||||
|
setPlayDirection((current) => (current === direction ? null : direction))
|
||||||
|
},
|
||||||
|
[currentKeys.length]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!playDirection) return
|
||||||
|
if (currentKeys.length < 2) {
|
||||||
|
setPlayDirection(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const timer = window.setInterval(() => {
|
||||||
|
const moved = stepVisibleImage(playDirection)
|
||||||
|
if (!moved) {
|
||||||
|
setPlayDirection(null)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.clearInterval(timer)
|
||||||
|
}
|
||||||
|
}, [currentKeys.length, playDirection, stepVisibleImage])
|
||||||
|
|
||||||
const getOperationSchema = useCallback(
|
const getOperationSchema = useCallback(
|
||||||
(operationId: string) => {
|
(operationId: string) => {
|
||||||
return (
|
return (
|
||||||
@@ -368,28 +445,32 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
|||||||
color="gray"
|
color="gray"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
scrollToIndex(0)
|
goToVisibleIndex(0)
|
||||||
currentKeys.length && setActiveImage(currentKeys[0])
|
|
||||||
setInputNumber("1")
|
|
||||||
setActiveImageId(1)
|
|
||||||
closeRightContext()
|
|
||||||
}}>
|
}}>
|
||||||
<IconChevronsLeft size={16} />
|
<IconChevronsLeft size={16} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
<ActionIcon
|
||||||
|
variant={playDirection === "backward" ? "filled" : "subtle"}
|
||||||
|
color={playDirection === "backward" ? "blue" : "gray"}
|
||||||
|
size="sm"
|
||||||
|
disabled={currentKeys.length < 2}
|
||||||
|
aria-label="向前播放"
|
||||||
|
title="向前播放"
|
||||||
|
onClick={() => {
|
||||||
|
togglePlayback("backward")
|
||||||
|
}}>
|
||||||
|
{playDirection === "backward" ? (
|
||||||
|
<IconPlayerTrackPrevFilled size={16} />
|
||||||
|
) : (
|
||||||
|
<IconPlayerTrackPrev size={16} />
|
||||||
|
)}
|
||||||
|
</ActionIcon>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="gray"
|
color="gray"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (inputNumber !== "1") {
|
stepVisibleImage("backward")
|
||||||
let newActiveIndex = +inputNumber - 1
|
|
||||||
setInputNumber(newActiveIndex.toString())
|
|
||||||
setActiveImageId(newActiveIndex)
|
|
||||||
scrollToIndex(newActiveIndex - 1)
|
|
||||||
|
|
||||||
setActiveImage(currentKeys?.[newActiveIndex - 1])
|
|
||||||
closeRightContext()
|
|
||||||
}
|
|
||||||
}}>
|
}}>
|
||||||
<IconChevronLeft size={16} />
|
<IconChevronLeft size={16} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
@@ -410,10 +491,21 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
scrollToIndex(+inputNumber - 1)
|
const targetIndex = Number(inputNumber)
|
||||||
setActiveImage(currentKeys?.[+inputNumber - 1])
|
if (
|
||||||
setActiveImageId(+inputNumber)
|
Number.isInteger(targetIndex) &&
|
||||||
closeRightContext()
|
targetIndex >= 1 &&
|
||||||
|
targetIndex <= currentKeys.length
|
||||||
|
) {
|
||||||
|
goToVisibleIndex(targetIndex - 1)
|
||||||
|
} else {
|
||||||
|
const currentIndex = currentKeys.findIndex(
|
||||||
|
(key) => key === activeImage
|
||||||
|
)
|
||||||
|
setInputNumber(
|
||||||
|
currentIndex >= 0 ? String(currentIndex + 1) : ""
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -423,27 +515,32 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
|||||||
color="gray"
|
color="gray"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (+inputNumber < currentKeys.length) {
|
stepVisibleImage("forward")
|
||||||
let newActiveIndex = +inputNumber + 1
|
|
||||||
setInputNumber(newActiveIndex.toString())
|
|
||||||
setActiveImageId(newActiveIndex)
|
|
||||||
scrollToIndex(newActiveIndex - 1)
|
|
||||||
setActiveImage(currentKeys?.[newActiveIndex - 1])
|
|
||||||
closeRightContext()
|
|
||||||
}
|
|
||||||
}}>
|
}}>
|
||||||
<IconChevronRight size={16} />
|
<IconChevronRight size={16} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
<ActionIcon
|
||||||
|
variant={playDirection === "forward" ? "filled" : "subtle"}
|
||||||
|
color={playDirection === "forward" ? "blue" : "gray"}
|
||||||
|
size="sm"
|
||||||
|
disabled={currentKeys.length < 2}
|
||||||
|
aria-label="向后播放"
|
||||||
|
title="向后播放"
|
||||||
|
onClick={() => {
|
||||||
|
togglePlayback("forward")
|
||||||
|
}}>
|
||||||
|
{playDirection === "forward" ? (
|
||||||
|
<IconPlayerTrackNextFilled size={16} />
|
||||||
|
) : (
|
||||||
|
<IconPlayerTrackNext size={16} />
|
||||||
|
)}
|
||||||
|
</ActionIcon>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="gray"
|
color="gray"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
scrollToIndex(currentKeys.length - 1)
|
goToVisibleIndex(currentKeys.length - 1)
|
||||||
setInputNumber(currentKeys.length.toString())
|
|
||||||
setActiveImageId(currentKeys.length)
|
|
||||||
setActiveImage(currentKeys?.[currentKeys.length - 1])
|
|
||||||
closeRightContext()
|
|
||||||
}}>
|
}}>
|
||||||
<IconChevronsRight size={16} />
|
<IconChevronsRight size={16} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
@@ -657,9 +754,7 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
|
|||||||
if (ctrlKey || shiftKey) {
|
if (ctrlKey || shiftKey) {
|
||||||
setSelectedItems(key, ctrlKey, shiftKey)
|
setSelectedItems(key, ctrlKey, shiftKey)
|
||||||
} else {
|
} else {
|
||||||
setInputNumber(index + 1 + "")
|
goToVisibleIndex(index)
|
||||||
setActiveImage(key)
|
|
||||||
setActiveImageId(index + 1)
|
|
||||||
setSelectedItems("", false, false)
|
setSelectedItems("", false, false)
|
||||||
}
|
}
|
||||||
closeRightContext()
|
closeRightContext()
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ const renderRaster = (paperGroup: paper.Group, option: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NormalizedPoint = [number, number]
|
type NormalizedPoint = [number, number]
|
||||||
|
type SupportBox = [number, number, number, number]
|
||||||
type ContextMenuLayout = {
|
type ContextMenuLayout = {
|
||||||
top: number
|
top: number
|
||||||
left: number
|
left: number
|
||||||
@@ -447,7 +448,8 @@ const PaperContainer = (
|
|||||||
async (
|
async (
|
||||||
points: Array<[number, number]>,
|
points: Array<[number, number]>,
|
||||||
tags: number[],
|
tags: number[],
|
||||||
clear_previous_state = false
|
clear_previous_state = false,
|
||||||
|
boxes: SupportBox[] = []
|
||||||
) => {
|
) => {
|
||||||
void clear_previous_state
|
void clear_previous_state
|
||||||
try {
|
try {
|
||||||
@@ -475,6 +477,9 @@ const PaperContainer = (
|
|||||||
usePaperStore.getState().rasterScale[activeImage] ?? 1
|
usePaperStore.getState().rasterScale[activeImage] ?? 1
|
||||||
|
|
||||||
const draw = () => {
|
const draw = () => {
|
||||||
|
const paperGroup = usePaperStore.getState().group!
|
||||||
|
const raster = paperGroup.getItems({ data: { name: "pic" } })[0]
|
||||||
|
|
||||||
points.forEach((point, index) => {
|
points.forEach((point, index) => {
|
||||||
const drawPoint = new paper.Path.Circle({
|
const drawPoint = new paper.Path.Circle({
|
||||||
center: point,
|
center: point,
|
||||||
@@ -486,16 +491,30 @@ const PaperContainer = (
|
|||||||
type: "point",
|
type: "point",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
drawPoint.parent = usePaperStore.getState().group!
|
drawPoint.parent = paperGroup
|
||||||
const raster = usePaperStore
|
|
||||||
.getState()
|
|
||||||
.group!.getItems({ data: { name: "pic" } })[0]
|
|
||||||
if (!raster.contains(drawPoint.position)) {
|
if (!raster.contains(drawPoint.position)) {
|
||||||
throw new Error("点位超出图片范围")
|
throw new Error("点位超出图片范围")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
boxes.forEach(([left, top, right, bottom]) => {
|
||||||
|
new paper.Path.Rectangle({
|
||||||
|
parent: paperGroup,
|
||||||
|
from: [left, top],
|
||||||
|
to: [right, bottom],
|
||||||
|
strokeColor: "#FFF",
|
||||||
|
strokeWidth: 2,
|
||||||
|
strokeScaling: false,
|
||||||
|
dashArray: [6, 4],
|
||||||
|
fillColor: new paper.Color(1, 1, 1, 0.05),
|
||||||
|
data: {
|
||||||
|
id: "support",
|
||||||
|
type: "box",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
console.log(points, tags)
|
console.log(points, tags, boxes)
|
||||||
clearSupportAnnotationItem()
|
clearSupportAnnotationItem()
|
||||||
draw()
|
draw()
|
||||||
|
|
||||||
@@ -512,8 +531,12 @@ const PaperContainer = (
|
|||||||
const promptPoints = foregroundPoints.length
|
const promptPoints = foregroundPoints.length
|
||||||
? foregroundPoints
|
? foregroundPoints
|
||||||
: normalizedPoints
|
: normalizedPoints
|
||||||
// const promptBox =
|
const normalizedBoxes = boxes.map(([left, top, right, bottom]) => [
|
||||||
// promptPoints.length > 1 ? getContourBox(promptPoints) : null
|
Math.round(Math.min(left, right) / currentScale),
|
||||||
|
Math.round(Math.min(top, bottom) / currentScale),
|
||||||
|
Math.round(Math.max(left, right) / currentScale),
|
||||||
|
Math.round(Math.max(top, bottom) / currentScale),
|
||||||
|
]) as SupportBox[]
|
||||||
const projectId =
|
const projectId =
|
||||||
projectDetail?.name ||
|
projectDetail?.name ||
|
||||||
projectDetail?.rpath ||
|
projectDetail?.rpath ||
|
||||||
@@ -522,8 +545,8 @@ const PaperContainer = (
|
|||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
throw new Error("缺少 project_id,无法调用模型接口")
|
throw new Error("缺少 project_id,无法调用模型接口")
|
||||||
}
|
}
|
||||||
if (!promptPoints.length) {
|
if (!promptPoints.length && !normalizedBoxes.length) {
|
||||||
throw new Error("缺少有效点位,无法调用模型接口")
|
throw new Error("缺少有效提示,无法调用模型接口")
|
||||||
}
|
}
|
||||||
// const picSize = usePaperStore.getState().rasterSize[activeImage]
|
// const picSize = usePaperStore.getState().rasterSize[activeImage]
|
||||||
|
|
||||||
@@ -533,9 +556,13 @@ const PaperContainer = (
|
|||||||
image: activeImage,
|
image: activeImage,
|
||||||
// project_id: "image_test_bk",
|
// project_id: "image_test_bk",
|
||||||
prompt: {
|
prompt: {
|
||||||
foreground_points: promptPoints,
|
...(promptPoints.length
|
||||||
background_points: background_points,
|
? { foreground_points: promptPoints }
|
||||||
// ...(promptBox ? { boxes: [promptBox] } : {}),
|
: {}),
|
||||||
|
...(background_points.length
|
||||||
|
? { background_points: background_points }
|
||||||
|
: {}),
|
||||||
|
...(normalizedBoxes.length ? { boxes: normalizedBoxes } : {}),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const { contours = [], hollows = [] } = data || {}
|
const { contours = [], hollows = [] } = data || {}
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ interface PaperState {
|
|||||||
renderPath: (
|
renderPath: (
|
||||||
points: Array<[number, number]>,
|
points: Array<[number, number]>,
|
||||||
tags: number[],
|
tags: number[],
|
||||||
flag?: boolean
|
flag?: boolean,
|
||||||
|
boxes?: Array<[number, number, number, number]>
|
||||||
) => void
|
) => void
|
||||||
) => void
|
) => void
|
||||||
getClickedCircle: (
|
getClickedCircle: (
|
||||||
@@ -2574,6 +2575,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
}
|
}
|
||||||
polygon.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
polygon.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (e.event && e.event.button === 2) {
|
if (e.event && e.event.button === 2) {
|
||||||
|
if (
|
||||||
|
["polygon", "brush", "point"].includes(
|
||||||
|
usePaperStore.getState().mode || ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
if (polygon.data.id) {
|
if (polygon.data.id) {
|
||||||
let category = useTopToolsStore
|
let category = useTopToolsStore
|
||||||
.getState()
|
.getState()
|
||||||
@@ -2620,6 +2627,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
point: paper.Point
|
point: paper.Point
|
||||||
}) => {
|
}) => {
|
||||||
if (e.event && e.event.button === 2) {
|
if (e.event && e.event.button === 2) {
|
||||||
|
if (
|
||||||
|
["polygon", "brush", "point"].includes(
|
||||||
|
usePaperStore.getState().mode || ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
if (compoundPath.data.id) {
|
if (compoundPath.data.id) {
|
||||||
let category = useTopToolsStore
|
let category = useTopToolsStore
|
||||||
.getState()
|
.getState()
|
||||||
@@ -2659,11 +2672,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let polygon: paper.Path | paper.CompoundPath | null
|
let polygon: paper.Path | paper.CompoundPath | null
|
||||||
let compoundPolygon: paper.Path | paper.CompoundPath | null
|
let compoundPolygon: paper.Path | paper.CompoundPath | null
|
||||||
let points: paper.Point[] = []
|
let points: paper.Point[] = []
|
||||||
let lastPoint: paper.Point
|
let lastPoint: paper.Point | null = null
|
||||||
let lastTime: number
|
let lastTime = 0
|
||||||
let isMouseDown: boolean = false
|
let isMouseDown: boolean = false
|
||||||
let polygonTool = paperPolygonTool
|
let polygonTool = paperPolygonTool
|
||||||
let currentMagnetPoint: paper.Point | null = null
|
let currentMagnetPoint: paper.Point | null = null
|
||||||
|
let draftId: number | null = null
|
||||||
|
|
||||||
const handleCircles = (path: paper.Path | paper.CompoundPath | null) => {
|
const handleCircles = (path: paper.Path | paper.CompoundPath | null) => {
|
||||||
// 更新选中的点
|
// 更新选中的点
|
||||||
@@ -2688,6 +2702,71 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDraftPolygonData = () => ({
|
||||||
|
imageId: useBottomToolsStore.getState().activeImage,
|
||||||
|
operationId: useTopToolsStore.getState().activeOperation,
|
||||||
|
id:
|
||||||
|
draftId ??
|
||||||
|
(draftId =
|
||||||
|
useRightToolsStore
|
||||||
|
.getState()
|
||||||
|
.pathIds.reduce((a, b) => Math.max(a, b), 0) + 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
const clearPolygonMagnet = () => {
|
||||||
|
const magnetPaths = usePaperStore
|
||||||
|
.getState()
|
||||||
|
.group!.getItems({ data: { type: "magnet" } })
|
||||||
|
magnetPaths.forEach((item) => {
|
||||||
|
item.remove()
|
||||||
|
})
|
||||||
|
currentMagnetPoint = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const rebuildPolygonDraft = () => {
|
||||||
|
polygon?.remove()
|
||||||
|
polygon = null
|
||||||
|
|
||||||
|
if (points.length) {
|
||||||
|
polygon = usePaperStore.getState().addPolygon(
|
||||||
|
renderPath,
|
||||||
|
points,
|
||||||
|
{
|
||||||
|
...usePaperStore.getState().toolOption,
|
||||||
|
},
|
||||||
|
getDraftPolygonData()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
draftId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCircles(polygon)
|
||||||
|
lastPoint = points.length
|
||||||
|
? usePaperStore
|
||||||
|
.getState()
|
||||||
|
.group!.localToGlobal(points[points.length - 1])
|
||||||
|
: null
|
||||||
|
clearPolygonMagnet()
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetPolygonDraft = () => {
|
||||||
|
polygon?.remove()
|
||||||
|
polygon = null
|
||||||
|
points = []
|
||||||
|
handleCircles(null)
|
||||||
|
lastPoint = null
|
||||||
|
lastTime = 0
|
||||||
|
draftId = null
|
||||||
|
clearPolygonMagnet()
|
||||||
|
}
|
||||||
|
|
||||||
|
const undoPolygonPoint = () => {
|
||||||
|
if (!points.length) return false
|
||||||
|
points.pop()
|
||||||
|
rebuildPolygonDraft()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const handlePolygonIntersectRaster = (savePath: paper.Path) => {
|
const handlePolygonIntersectRaster = (savePath: paper.Path) => {
|
||||||
let oldPolygon: any = savePath
|
let oldPolygon: any = savePath
|
||||||
polygon = oldPolygon.intersect(
|
polygon = oldPolygon.intersect(
|
||||||
@@ -2781,6 +2860,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
point: paper.Point
|
point: paper.Point
|
||||||
}) => {
|
}) => {
|
||||||
if (startMiddleMousePan(e.event)) return
|
if (startMiddleMousePan(e.event)) return
|
||||||
|
if (e.event.button === 2) {
|
||||||
|
e.event.preventDefault()
|
||||||
|
hidePaperContextMenu()
|
||||||
|
undoPolygonPoint()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (e.event.button !== 0) return
|
if (e.event.button !== 0) return
|
||||||
isMouseDown = true
|
isMouseDown = true
|
||||||
|
|
||||||
@@ -2803,6 +2888,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
selectedCircles.forEach((item) => {
|
selectedCircles.forEach((item) => {
|
||||||
item.remove()
|
item.remove()
|
||||||
})
|
})
|
||||||
|
selectedCircles = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调整polygon点位初始顺序
|
// 调整polygon点位初始顺序
|
||||||
@@ -3000,15 +3086,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
selectedCircles.forEach((item) => {
|
selectedCircles.forEach((item) => {
|
||||||
item.remove()
|
item.remove()
|
||||||
})
|
})
|
||||||
const magnetPaths = usePaperStore
|
selectedCircles = []
|
||||||
.getState()
|
clearPolygonMagnet()
|
||||||
.group!.getItems({ data: { type: "magnet" } })
|
|
||||||
magnetPaths.forEach((item) => {
|
|
||||||
item.remove()
|
|
||||||
})
|
|
||||||
currentMagnetPoint = null
|
|
||||||
|
|
||||||
useTopToolsStore.getState().setPressA(false)
|
useTopToolsStore.getState().setPressA(false)
|
||||||
|
draftId = null
|
||||||
|
lastPoint = null
|
||||||
|
lastTime = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -3036,14 +3119,15 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let point = currentMagnetPoint
|
let point = currentMagnetPoint
|
||||||
? currentMagnetPoint
|
? currentMagnetPoint
|
||||||
: temp.globalToLocal(e.point)
|
: temp.globalToLocal(e.point)
|
||||||
|
const draftData = getDraftPolygonData()
|
||||||
|
|
||||||
let circle = usePaperStore
|
let circle = usePaperStore
|
||||||
.getState()
|
.getState()
|
||||||
.getClickedCircle(
|
.getClickedCircle(
|
||||||
point,
|
point,
|
||||||
selectedCircles.length,
|
selectedCircles.length,
|
||||||
polygon?.data.id,
|
draftData.id,
|
||||||
polygon?.data.blankColor
|
usePaperStore.getState().toolOption.blankColor
|
||||||
)
|
)
|
||||||
// add circle
|
// add circle
|
||||||
selectedCircles.push(circle)
|
selectedCircles.push(circle)
|
||||||
@@ -3060,14 +3144,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// option
|
// option
|
||||||
...usePaperStore.getState().toolOption,
|
...usePaperStore.getState().toolOption,
|
||||||
},
|
},
|
||||||
{
|
draftData
|
||||||
imageId: useBottomToolsStore.getState().activeImage,
|
|
||||||
operationId: useTopToolsStore.getState().activeOperation,
|
|
||||||
id:
|
|
||||||
useRightToolsStore
|
|
||||||
.getState()
|
|
||||||
.pathIds.reduce((a, b) => Math.max(a, b), 0) + 1,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
lastPoint = checkPoint
|
lastPoint = checkPoint
|
||||||
@@ -3139,7 +3216,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (isMouseDown) {
|
if (isMouseDown && lastPoint) {
|
||||||
const delta = currentMagnetPoint
|
const delta = currentMagnetPoint
|
||||||
? (currentMagnetPoint as paper.Point).subtract(lastPoint)
|
? (currentMagnetPoint as paper.Point).subtract(lastPoint)
|
||||||
: e.point.subtract(lastPoint)
|
: e.point.subtract(lastPoint)
|
||||||
@@ -3187,27 +3264,17 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// option
|
// option
|
||||||
...usePaperStore.getState().toolOption,
|
...usePaperStore.getState().toolOption,
|
||||||
},
|
},
|
||||||
{}
|
getDraftPolygonData()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
polygonTool.onKeyDown = (e: paper.KeyEvent) => {
|
polygonTool.onKeyDown = (e: paper.KeyEvent) => {
|
||||||
if (e.key === "escape" || e.key === "alt") {
|
if (e.key === "escape" || e.key === "alt") {
|
||||||
if (polygon) {
|
if (polygon || points.length) {
|
||||||
polygon.remove()
|
resetPolygonDraft()
|
||||||
polygon = null
|
usePaperSupportStore.getState().handleBufferPaths(null)
|
||||||
points = []
|
|
||||||
usePaperSupportStore.getState().handleBufferPaths(polygon)
|
|
||||||
handleCircles(polygon)
|
|
||||||
}
|
}
|
||||||
const magnetPaths = usePaperStore
|
|
||||||
.getState()
|
|
||||||
.group!.getItems({ data: { type: "magnet" } })
|
|
||||||
magnetPaths.forEach((item) => {
|
|
||||||
item.remove()
|
|
||||||
})
|
|
||||||
currentMagnetPoint = null
|
|
||||||
} else if (e.key === "delete") {
|
} else if (e.key === "delete") {
|
||||||
handleDelete()
|
handleDelete()
|
||||||
forceUpdate()
|
forceUpdate()
|
||||||
@@ -3255,6 +3322,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
}
|
}
|
||||||
rect.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
rect.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (e.event && e.event.button === 2) {
|
if (e.event && e.event.button === 2) {
|
||||||
|
if (
|
||||||
|
["polygon", "brush", "point"].includes(
|
||||||
|
usePaperStore.getState().mode || ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
if (rect.data.id) {
|
if (rect.data.id) {
|
||||||
let category = useTopToolsStore
|
let category = useTopToolsStore
|
||||||
.getState()
|
.getState()
|
||||||
@@ -3469,6 +3542,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// });
|
// });
|
||||||
brush.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
brush.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (e.event && e.event.button === 2) {
|
if (e.event && e.event.button === 2) {
|
||||||
|
if (
|
||||||
|
["polygon", "brush", "point"].includes(
|
||||||
|
usePaperStore.getState().mode || ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
// console.log(e.event.clientX, e.event.clientY);
|
// console.log(e.event.clientX, e.event.clientY);
|
||||||
if (brush.data.id) {
|
if (brush.data.id) {
|
||||||
let category = useTopToolsStore
|
let category = useTopToolsStore
|
||||||
@@ -3506,10 +3585,66 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let brushCircles: paper.Path.Circle[] = []
|
let brushCircles: paper.Path.Circle[] = []
|
||||||
let myPath: paper.Path | null
|
let myPath: paper.Path | null
|
||||||
let compoundPolygon: paper.Path | paper.CompoundPath | null
|
let compoundPolygon: paper.Path | paper.CompoundPath | null
|
||||||
let lastPoint: paper.Point
|
let lastPoint: paper.Point | null = null
|
||||||
let points: paper.Point[] = []
|
let points: paper.Point[] = []
|
||||||
|
|
||||||
let brushTool = paperBrushTool
|
let brushTool = paperBrushTool
|
||||||
|
let draftId: number | null = null
|
||||||
|
|
||||||
|
const getDraftBrushData = () => ({
|
||||||
|
imageId: useBottomToolsStore.getState().activeImage,
|
||||||
|
operationId: useTopToolsStore.getState().activeOperation,
|
||||||
|
id:
|
||||||
|
draftId ??
|
||||||
|
(draftId =
|
||||||
|
useRightToolsStore
|
||||||
|
.getState()
|
||||||
|
.pathIds.reduce((a, b) => Math.max(a, b), 0) + 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
const rebuildBrushDraft = () => {
|
||||||
|
myPath?.remove()
|
||||||
|
myPath = null
|
||||||
|
|
||||||
|
if (points.length) {
|
||||||
|
myPath = usePaperStore.getState().addBrush(
|
||||||
|
renderPath,
|
||||||
|
points,
|
||||||
|
{
|
||||||
|
...usePaperStore.getState().toolOption,
|
||||||
|
},
|
||||||
|
getDraftBrushData()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
draftId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPoint = points.length
|
||||||
|
? usePaperStore
|
||||||
|
.getState()
|
||||||
|
.group!.localToGlobal(points[points.length - 1])
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetBrushDraft = () => {
|
||||||
|
brushCircles.forEach((item) => {
|
||||||
|
item.remove()
|
||||||
|
})
|
||||||
|
brushCircles = []
|
||||||
|
myPath?.remove()
|
||||||
|
myPath = null
|
||||||
|
points = []
|
||||||
|
lastPoint = null
|
||||||
|
draftId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const undoBrushPoint = () => {
|
||||||
|
if (!points.length) return false
|
||||||
|
points.pop()
|
||||||
|
brushCircles.pop()?.remove()
|
||||||
|
rebuildBrushDraft()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const handleBrushIntersectRaster = (savePath: paper.Path) => {
|
const handleBrushIntersectRaster = (savePath: paper.Path) => {
|
||||||
let intersections = savePath.getIntersections(
|
let intersections = savePath.getIntersections(
|
||||||
@@ -3576,6 +3711,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
|
|
||||||
brushTool.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
brushTool.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (startMiddleMousePan(e.event)) return
|
if (startMiddleMousePan(e.event)) return
|
||||||
|
if (e.event.button === 2) {
|
||||||
|
e.event.preventDefault()
|
||||||
|
hidePaperContextMenu()
|
||||||
|
undoBrushPoint()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (e.event.button !== 0) return
|
if (e.event.button !== 0) return
|
||||||
|
|
||||||
// 绘制时去除选中辅助
|
// 绘制时去除选中辅助
|
||||||
@@ -3771,8 +3912,11 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
brushCircles.forEach((item) => {
|
brushCircles.forEach((item) => {
|
||||||
item.remove()
|
item.remove()
|
||||||
})
|
})
|
||||||
|
brushCircles = []
|
||||||
|
|
||||||
useTopToolsStore.getState().setPressA(false)
|
useTopToolsStore.getState().setPressA(false)
|
||||||
|
draftId = null
|
||||||
|
lastPoint = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -3790,6 +3934,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
|
|
||||||
let temp = renderPath(usePaperStore.getState().group!, {})
|
let temp = renderPath(usePaperStore.getState().group!, {})
|
||||||
let point = temp.globalToLocal(e.point)
|
let point = temp.globalToLocal(e.point)
|
||||||
|
const draftData = getDraftBrushData()
|
||||||
|
|
||||||
// add point
|
// add point
|
||||||
points.push(point)
|
points.push(point)
|
||||||
@@ -3803,22 +3948,15 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// option
|
// option
|
||||||
...usePaperStore.getState().toolOption,
|
...usePaperStore.getState().toolOption,
|
||||||
},
|
},
|
||||||
{
|
draftData
|
||||||
imageId: useBottomToolsStore.getState().activeImage,
|
|
||||||
operationId: useTopToolsStore.getState().activeOperation,
|
|
||||||
id:
|
|
||||||
useRightToolsStore
|
|
||||||
.getState()
|
|
||||||
.pathIds.reduce((a, b) => Math.max(a, b), 0) + 1,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
let circle = usePaperStore
|
let circle = usePaperStore
|
||||||
.getState()
|
.getState()
|
||||||
.getClickedCircle(
|
.getClickedCircle(
|
||||||
point,
|
point,
|
||||||
brushCircles.length,
|
brushCircles.length,
|
||||||
myPath?.data.id,
|
draftData.id,
|
||||||
myPath?.data.blankColor
|
usePaperStore.getState().toolOption.blankColor
|
||||||
)
|
)
|
||||||
brushCircles.push(circle)
|
brushCircles.push(circle)
|
||||||
|
|
||||||
@@ -3846,7 +3984,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
fillColor: undefined,
|
fillColor: undefined,
|
||||||
closed: false,
|
closed: false,
|
||||||
},
|
},
|
||||||
{}
|
getDraftBrushData()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3857,13 +3995,8 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
|
|
||||||
brushTool.onKeyDown = (e: paper.KeyEvent) => {
|
brushTool.onKeyDown = (e: paper.KeyEvent) => {
|
||||||
if (e.key === "escape" || e.key === "alt") {
|
if (e.key === "escape" || e.key === "alt") {
|
||||||
if (myPath) {
|
if (myPath || points.length) {
|
||||||
brushCircles.forEach((item) => {
|
resetBrushDraft()
|
||||||
item.remove()
|
|
||||||
})
|
|
||||||
myPath.remove()
|
|
||||||
myPath = null
|
|
||||||
points = []
|
|
||||||
}
|
}
|
||||||
} else if (e.key === "delete") {
|
} else if (e.key === "delete") {
|
||||||
handleDelete()
|
handleDelete()
|
||||||
@@ -3900,6 +4033,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
myCircle.data = Object.assign({ type: "circle", ...option }, data)
|
myCircle.data = Object.assign({ type: "circle", ...option }, data)
|
||||||
myCircle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
myCircle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (e.event && e.event.button === 2) {
|
if (e.event && e.event.button === 2) {
|
||||||
|
if (
|
||||||
|
["polygon", "brush", "point"].includes(
|
||||||
|
usePaperStore.getState().mode || ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
// console.log(e.event.clientX, e.event.clientY);
|
// console.log(e.event.clientX, e.event.clientY);
|
||||||
if (myCircle.data.id) {
|
if (myCircle.data.id) {
|
||||||
let category = useTopToolsStore
|
let category = useTopToolsStore
|
||||||
@@ -3973,13 +4112,82 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let myCircleText: paper.PointText[] = []
|
let myCircleText: paper.PointText[] = []
|
||||||
let textNumbers: number[] = []
|
let textNumbers: number[] = []
|
||||||
let myPath: paper.Path | null
|
let myPath: paper.Path | null
|
||||||
let lastPoint: paper.Point
|
let lastPoint: paper.Point | null = null
|
||||||
let points: paper.Point[] = []
|
let points: paper.Point[] = []
|
||||||
|
|
||||||
let pointTool = paperPointTool
|
let pointTool = paperPointTool
|
||||||
|
let draftId: number | null = null
|
||||||
|
|
||||||
|
const getDraftPointData = () => ({
|
||||||
|
imageId: useBottomToolsStore.getState().activeImage,
|
||||||
|
operationId: useTopToolsStore.getState().activeOperation,
|
||||||
|
id:
|
||||||
|
draftId ??
|
||||||
|
(draftId =
|
||||||
|
useRightToolsStore
|
||||||
|
.getState()
|
||||||
|
.pathIds.reduce((a, b) => Math.max(a, b), 0) + 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
const rebuildPointDraft = () => {
|
||||||
|
myPath?.remove()
|
||||||
|
myPath = null
|
||||||
|
|
||||||
|
if (points.length) {
|
||||||
|
myPath = usePaperStore.getState().addPointLine(
|
||||||
|
renderPath,
|
||||||
|
points,
|
||||||
|
{
|
||||||
|
...usePaperStore.getState().toolOption,
|
||||||
|
},
|
||||||
|
getDraftPointData()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
draftId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPoint = points.length
|
||||||
|
? usePaperStore
|
||||||
|
.getState()
|
||||||
|
.group!.localToGlobal(points[points.length - 1])
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetPointDraft = () => {
|
||||||
|
myPath?.remove()
|
||||||
|
myPath = null
|
||||||
|
points = []
|
||||||
|
textNumbers = []
|
||||||
|
myCircleText.forEach((item) => {
|
||||||
|
item.remove()
|
||||||
|
})
|
||||||
|
myCircleText = []
|
||||||
|
myCircle.forEach((item) => {
|
||||||
|
item.remove()
|
||||||
|
})
|
||||||
|
myCircle = []
|
||||||
|
lastPoint = null
|
||||||
|
draftId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const undoPoint = () => {
|
||||||
|
if (!points.length) return false
|
||||||
|
points.pop()
|
||||||
|
textNumbers.pop()
|
||||||
|
myCircle.pop()?.remove()
|
||||||
|
myCircleText.pop()?.remove()
|
||||||
|
rebuildPointDraft()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
pointTool.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
pointTool.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (startMiddleMousePan(e.event)) return
|
if (startMiddleMousePan(e.event)) return
|
||||||
|
if (e.event.button === 2) {
|
||||||
|
e.event.preventDefault()
|
||||||
|
hidePaperContextMenu()
|
||||||
|
undoPoint()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (e.event.button !== 0) return
|
if (e.event.button !== 0) return
|
||||||
|
|
||||||
// 绘制时去除选中辅助
|
// 绘制时去除选中辅助
|
||||||
@@ -4106,6 +4314,8 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
myCircleText = []
|
myCircleText = []
|
||||||
// 避免后续按键删除已绘制的circle
|
// 避免后续按键删除已绘制的circle
|
||||||
myCircle = []
|
myCircle = []
|
||||||
|
draftId = null
|
||||||
|
lastPoint = null
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -4125,6 +4335,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let temp = renderCircle(usePaperStore.getState().group!, {})
|
let temp = renderCircle(usePaperStore.getState().group!, {})
|
||||||
let point = temp.globalToLocal(e.point)
|
let point = temp.globalToLocal(e.point)
|
||||||
let text = textNumbers.reduce((a, b) => Math.max(a, b), 0) + 1
|
let text = textNumbers.reduce((a, b) => Math.max(a, b), 0) + 1
|
||||||
|
const draftData = getDraftPointData()
|
||||||
|
|
||||||
let circle = usePaperStore.getState().addPoint(
|
let circle = usePaperStore.getState().addPoint(
|
||||||
renderCircle,
|
renderCircle,
|
||||||
@@ -4137,13 +4348,8 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
strokeScaling: false,
|
strokeScaling: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
imageId: useBottomToolsStore.getState().activeImage,
|
...draftData,
|
||||||
operationId: useTopToolsStore.getState().activeOperation,
|
|
||||||
text: text,
|
text: text,
|
||||||
id:
|
|
||||||
useRightToolsStore
|
|
||||||
.getState()
|
|
||||||
.pathIds.reduce((a, b) => Math.max(a, b), 0) + 1,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// add circle
|
// add circle
|
||||||
@@ -4174,14 +4380,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// option
|
// option
|
||||||
...usePaperStore.getState().toolOption,
|
...usePaperStore.getState().toolOption,
|
||||||
},
|
},
|
||||||
{
|
draftData
|
||||||
imageId: useBottomToolsStore.getState().activeImage,
|
|
||||||
operationId: useTopToolsStore.getState().activeOperation,
|
|
||||||
id:
|
|
||||||
useRightToolsStore
|
|
||||||
.getState()
|
|
||||||
.pathIds.reduce((a, b) => Math.max(a, b), 0) + 1,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
lastPoint = e.point
|
lastPoint = e.point
|
||||||
@@ -4208,7 +4407,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
fillColor: undefined,
|
fillColor: undefined,
|
||||||
closed: false,
|
closed: false,
|
||||||
},
|
},
|
||||||
{}
|
getDraftPointData()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4217,19 +4416,8 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
}
|
}
|
||||||
pointTool.onKeyDown = (e: paper.KeyEvent) => {
|
pointTool.onKeyDown = (e: paper.KeyEvent) => {
|
||||||
if (e.key === "escape" || e.key === "alt") {
|
if (e.key === "escape" || e.key === "alt") {
|
||||||
if (myPath) {
|
if (myPath || points.length) {
|
||||||
myPath.remove()
|
resetPointDraft()
|
||||||
myPath = null
|
|
||||||
points = []
|
|
||||||
textNumbers = []
|
|
||||||
myCircleText.forEach((item) => {
|
|
||||||
item.remove()
|
|
||||||
})
|
|
||||||
myCircleText = []
|
|
||||||
myCircle.forEach((item) => {
|
|
||||||
item.remove()
|
|
||||||
})
|
|
||||||
myCircle = []
|
|
||||||
}
|
}
|
||||||
} else if (e.key === "delete") {
|
} else if (e.key === "delete") {
|
||||||
handleDelete()
|
handleDelete()
|
||||||
@@ -4283,6 +4471,12 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
circle.scale(newScale / currentScaling)
|
circle.scale(newScale / currentScaling)
|
||||||
circle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
circle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (e.event && e.event.button === 2) {
|
if (e.event && e.event.button === 2) {
|
||||||
|
if (
|
||||||
|
["polygon", "brush", "point"].includes(
|
||||||
|
usePaperStore.getState().mode || ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
// console.log(e.event.clientX, e.event.clientY);
|
// console.log(e.event.clientX, e.event.clientY);
|
||||||
if (circle.data.id) {
|
if (circle.data.id) {
|
||||||
let category = useTopToolsStore
|
let category = useTopToolsStore
|
||||||
@@ -4325,34 +4519,125 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
initSupportTool: (tool, renderPath) => {
|
initSupportTool: (tool, renderPath) => {
|
||||||
let points: Array<[number, number]> = []
|
let points: Array<[number, number]> = []
|
||||||
let tags: number[] = []
|
let tags: number[] = []
|
||||||
|
let boxes: Array<[number, number, number, number]> = []
|
||||||
|
let dragStartPoint: paper.Point | null = null
|
||||||
|
let dragButton: number | null = null
|
||||||
|
let supportBox: paper.Path.Rectangle | null = null
|
||||||
|
let isBoxDragging = false
|
||||||
|
|
||||||
|
const BOX_DRAG_THRESHOLD = 6
|
||||||
|
|
||||||
|
const clearSupportBoxPreview = () => {
|
||||||
|
supportBox?.remove()
|
||||||
|
supportBox = null
|
||||||
|
isBoxDragging = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSupportBox = (
|
||||||
|
from: paper.Point,
|
||||||
|
to: paper.Point
|
||||||
|
): [number, number, number, number] => [
|
||||||
|
Math.min(from.x, to.x),
|
||||||
|
Math.min(from.y, to.y),
|
||||||
|
Math.max(from.x, to.x),
|
||||||
|
Math.max(from.y, to.y),
|
||||||
|
]
|
||||||
|
|
||||||
|
const renderSupportBoxPreview = (from: paper.Point, to: paper.Point) => {
|
||||||
|
const [left, top, right, bottom] = getSupportBox(from, to)
|
||||||
|
supportBox?.remove()
|
||||||
|
supportBox = new paper.Path.Rectangle({
|
||||||
|
parent: usePaperStore.getState().group!,
|
||||||
|
from: [left, top],
|
||||||
|
to: [right, bottom],
|
||||||
|
strokeColor: "#FFF",
|
||||||
|
strokeWidth: 2,
|
||||||
|
strokeScaling: false,
|
||||||
|
dashArray: [6, 4],
|
||||||
|
fillColor: new paper.Color(1, 1, 1, 0.05),
|
||||||
|
data: {
|
||||||
|
id: "support",
|
||||||
|
type: "box",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderSupportPrompt = () => {
|
||||||
|
void renderPath(points, tags, points.length + boxes.length === 1, boxes)
|
||||||
|
}
|
||||||
|
|
||||||
tool.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
tool.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
|
||||||
if (startMiddleMousePan(e.event)) return
|
if (startMiddleMousePan(e.event)) return
|
||||||
let point = usePaperStore.getState().group!.globalToLocal(e.point)
|
dragStartPoint = usePaperStore.getState().group!.globalToLocal(e.point)
|
||||||
points.push([point.x, point.y])
|
dragButton = e.event.button
|
||||||
tags.push(e.event.button === 0 ? 1 : 0)
|
clearSupportBoxPreview()
|
||||||
renderPath(points, tags, points.length === 1)
|
|
||||||
}
|
}
|
||||||
tool.onMouseDrag = (e: { event: MouseEvent; delta: paper.Point }) => {
|
|
||||||
|
tool.onMouseDrag = (e: {
|
||||||
|
event: MouseEvent
|
||||||
|
point: paper.Point
|
||||||
|
delta: paper.Point
|
||||||
|
}) => {
|
||||||
if (dragMiddleMousePan(e)) return
|
if (dragMiddleMousePan(e)) return
|
||||||
|
|
||||||
|
if (!dragStartPoint || dragButton !== 0) return
|
||||||
|
|
||||||
|
const currentPoint = usePaperStore
|
||||||
|
.getState()
|
||||||
|
.group!.globalToLocal(e.point)
|
||||||
|
if (currentPoint.subtract(dragStartPoint).length <= BOX_DRAG_THRESHOLD)
|
||||||
|
return
|
||||||
|
|
||||||
|
isBoxDragging = true
|
||||||
|
renderSupportBoxPreview(dragStartPoint, currentPoint)
|
||||||
}
|
}
|
||||||
tool.onMouseUp = (_e: paper.ToolEvent) => {
|
|
||||||
stopMiddleMousePan()
|
tool.onMouseUp = (e: paper.ToolEvent) => {
|
||||||
|
if (stopMiddleMousePan()) return
|
||||||
|
if (!dragStartPoint || dragButton === null) return
|
||||||
|
|
||||||
|
const currentPoint = usePaperStore
|
||||||
|
.getState()
|
||||||
|
.group!.globalToLocal(e.point)
|
||||||
|
|
||||||
|
if (dragButton === 0 && isBoxDragging) {
|
||||||
|
boxes.push(getSupportBox(dragStartPoint, currentPoint))
|
||||||
|
} else {
|
||||||
|
points.push([dragStartPoint.x, dragStartPoint.y])
|
||||||
|
tags.push(dragButton === 0 ? 1 : 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSupportPrompt()
|
||||||
|
clearSupportBoxPreview()
|
||||||
|
dragStartPoint = null
|
||||||
|
dragButton = null
|
||||||
}
|
}
|
||||||
|
|
||||||
tool.onKeyDown = (e: paper.KeyEvent) => {
|
tool.onKeyDown = (e: paper.KeyEvent) => {
|
||||||
if (e.key === "escape") {
|
if (e.key === "escape") {
|
||||||
points = []
|
points = []
|
||||||
tags = []
|
tags = []
|
||||||
|
boxes = []
|
||||||
|
dragStartPoint = null
|
||||||
|
dragButton = null
|
||||||
|
clearSupportBoxPreview()
|
||||||
clearSupportAnnotationItem()
|
clearSupportAnnotationItem()
|
||||||
usePaperStore.getState().setPaperMode("pan")
|
usePaperStore.getState().setPaperMode("pan")
|
||||||
}
|
}
|
||||||
if (e.key === "f1") {
|
if (e.key === "f1") {
|
||||||
points = []
|
points = []
|
||||||
tags = []
|
tags = []
|
||||||
|
boxes = []
|
||||||
|
dragStartPoint = null
|
||||||
|
dragButton = null
|
||||||
|
clearSupportBoxPreview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tool.onMouseMove = (e: paper.MouseEvent) => {
|
||||||
|
handleMouseMove(e)
|
||||||
|
}
|
||||||
|
|
||||||
set((state: PaperState) => ({
|
set((state: PaperState) => ({
|
||||||
...state,
|
...state,
|
||||||
supportTool: tool,
|
supportTool: tool,
|
||||||
|
|||||||
Reference in New Issue
Block a user