feat(shortcut): ctrl+f
This commit is contained in:
@@ -1376,6 +1376,141 @@ const LabelPage = ({
|
|||||||
[getOperationSchema, setActiveOperation]
|
[getOperationSchema, setActiveOperation]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleMergeSelectedObjects = useCallback(() => {
|
||||||
|
const activeImage = useBottomToolsStore.getState().activeImage
|
||||||
|
const selectedIds =
|
||||||
|
useObjectStore.getState().selectedPath[activeImage] || []
|
||||||
|
if (selectedIds.length <= 1) {
|
||||||
|
notifications.show({
|
||||||
|
color: "yellow",
|
||||||
|
message: "请先选中同类别且数量大于1的对象",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextLabel = safeClone(useLabelStore.getState().label)
|
||||||
|
const imageLabel = nextLabel.get(activeImage)
|
||||||
|
if (!imageLabel) return
|
||||||
|
|
||||||
|
const selectedIdSet = new Set(selectedIds)
|
||||||
|
const selectedByOperation = Array.from(imageLabel.entries())
|
||||||
|
.map(([operationId, paths]) => {
|
||||||
|
const selectedPaths = paths.filter((item) => selectedIdSet.has(item[0]))
|
||||||
|
return { operationId, selectedPaths }
|
||||||
|
})
|
||||||
|
.filter((item) => item.selectedPaths.length > 0)
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectedByOperation.length !== 1 ||
|
||||||
|
selectedByOperation[0].selectedPaths.length <= 1 ||
|
||||||
|
selectedByOperation[0].selectedPaths.length !== selectedIds.length
|
||||||
|
) {
|
||||||
|
notifications.show({
|
||||||
|
color: "yellow",
|
||||||
|
message: "仅支持合并同类别且数量大于1的选中对象",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { operationId, selectedPaths } = selectedByOperation[0]
|
||||||
|
const selectedTypeList = selectedIds
|
||||||
|
.map((id) => usePaperStore.getState().getItemById(id)?.data?.type)
|
||||||
|
.filter((type): type is string => typeof type === "string")
|
||||||
|
const selectedTypeSet = new Set(selectedTypeList)
|
||||||
|
const selectedType = Array.from(selectedTypeSet)[0]
|
||||||
|
const operationSchema = getOperationSchema(operationId)
|
||||||
|
const operationType = operationSchema
|
||||||
|
? labelTypeMap.get(operationSchema.label_type)
|
||||||
|
: ""
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectedTypeList.length !== selectedIds.length ||
|
||||||
|
selectedTypeSet.size !== 1 ||
|
||||||
|
!["polygon", "brush"].includes(selectedType) ||
|
||||||
|
!["多边形", "多线段"].includes(operationType || "")
|
||||||
|
) {
|
||||||
|
notifications.show({
|
||||||
|
color: "yellow",
|
||||||
|
message: "仅支持合并 polygon/brush 类型对象",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedSelectedPaths = [...selectedPaths].sort((a, b) => a[0] - b[0])
|
||||||
|
const targetId = sortedSelectedPaths[0][0]
|
||||||
|
const mergedPaths = sortedSelectedPaths.flatMap((item) =>
|
||||||
|
safeClone(item[1] || [])
|
||||||
|
)
|
||||||
|
const mergedCompoundPaths = sortedSelectedPaths.flatMap((item) =>
|
||||||
|
safeClone(item[3] || [])
|
||||||
|
)
|
||||||
|
const mergedDetail = {
|
||||||
|
...(safeClone(sortedSelectedPaths[0][2]) || {}),
|
||||||
|
parentGroupId: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergedData: [number, any[], any, any[]] = [
|
||||||
|
targetId,
|
||||||
|
mergedPaths,
|
||||||
|
mergedDetail,
|
||||||
|
mergedCompoundPaths,
|
||||||
|
]
|
||||||
|
|
||||||
|
const restPaths = (imageLabel.get(operationId) || []).filter(
|
||||||
|
(item) => !selectedIdSet.has(item[0])
|
||||||
|
)
|
||||||
|
imageLabel.set(
|
||||||
|
operationId,
|
||||||
|
[...restPaths, mergedData].sort((a, b) => a[0] - b[0])
|
||||||
|
)
|
||||||
|
|
||||||
|
const nextPathGroupMap = safeClone(
|
||||||
|
useRightToolsStore.getState().pathGroupMap
|
||||||
|
)
|
||||||
|
const imageGroupMap = nextPathGroupMap.get(activeImage)
|
||||||
|
if (imageGroupMap) {
|
||||||
|
for (let [groupId, pathIds] of imageGroupMap.entries()) {
|
||||||
|
const nextPathIds = pathIds.filter((id) => !selectedIdSet.has(id))
|
||||||
|
if (nextPathIds.length > 1) {
|
||||||
|
imageGroupMap.set(groupId, nextPathIds)
|
||||||
|
} else {
|
||||||
|
imageGroupMap.delete(groupId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!imageGroupMap.size) nextPathGroupMap.delete(activeImage)
|
||||||
|
useRightToolsStore.getState().setPathGroupMap(nextPathGroupMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
useLabelStore.getState().setLabel(nextLabel)
|
||||||
|
useLabelStore.getState().pushStateStack(nextLabel)
|
||||||
|
useObjectStore.setState((state) => ({
|
||||||
|
...state,
|
||||||
|
selectedPath: {
|
||||||
|
...state.selectedPath,
|
||||||
|
[activeImage]: [targetId],
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const paperGroup = usePaperStore.getState().group
|
||||||
|
if (paperGroup) {
|
||||||
|
paperGroup
|
||||||
|
.getItems({})
|
||||||
|
.filter((item) => item.data.id)
|
||||||
|
.forEach((item) => {
|
||||||
|
item.remove()
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const [categoryId] of imageLabel.entries()) {
|
||||||
|
paperContainerRef.current?.renderPolygons?.(categoryId, imageLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifications.show({
|
||||||
|
color: "green",
|
||||||
|
message: `已合并${selectedIds.length}个对象`,
|
||||||
|
})
|
||||||
|
}, [getOperationSchema])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const down = (e: KeyboardEvent) => {
|
const down = (e: KeyboardEvent) => {
|
||||||
// console.log(e, e.key);
|
// console.log(e, e.key);
|
||||||
@@ -1384,6 +1519,18 @@ const LabelPage = ({
|
|||||||
console.log(e.key)
|
console.log(e.key)
|
||||||
const mode = usePaperStore.getState().mode
|
const mode = usePaperStore.getState().mode
|
||||||
|
|
||||||
|
if (e.altKey && e.key.toLowerCase() === "x") {
|
||||||
|
e.preventDefault()
|
||||||
|
if (!isView) topRef.current?.commitTaskByActionKey?.()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.ctrlKey && e.key.toLowerCase() === "f") {
|
||||||
|
e.preventDefault()
|
||||||
|
handleMergeSelectedObjects()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (e.key === "Alt" && mode !== "support") {
|
if (e.key === "Alt" && mode !== "support") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
// console.log(e.key);
|
// console.log(e.key);
|
||||||
@@ -1601,6 +1748,7 @@ const LabelPage = ({
|
|||||||
editMode,
|
editMode,
|
||||||
focusInput,
|
focusInput,
|
||||||
getOperationSchema,
|
getOperationSchema,
|
||||||
|
handleMergeSelectedObjects,
|
||||||
handleShortcut,
|
handleShortcut,
|
||||||
isView,
|
isView,
|
||||||
projectDetail?.label_type,
|
projectDetail?.label_type,
|
||||||
|
|||||||
@@ -1638,6 +1638,7 @@ const TopTools = (
|
|||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
handleSave,
|
handleSave,
|
||||||
handleBackup,
|
handleBackup,
|
||||||
|
commitTaskByActionKey,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const renderEditIcon = useMemo(() => {
|
const renderEditIcon = useMemo(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user