diff --git a/packages/editor/src/language/zh-CN-en-US.ts b/packages/editor/src/language/zh-CN-en-US.ts index c3407bb..2063e27 100644 --- a/packages/editor/src/language/zh-CN-en-US.ts +++ b/packages/editor/src/language/zh-CN-en-US.ts @@ -122,6 +122,9 @@ export default { world: '世界坐标', "Status monitoring":"状态监控", }, + path: { + "Path drawing: Left-click to add a point, and double-click to end the drawing": "路径绘制:左键单击添加点,双击结束绘制", + }, viewportInfo: { Objects: '物体', Vertices: '顶点', @@ -754,6 +757,7 @@ export default { 'Query failed': "查询失败", 'Related document': "相关文档", Copy: "复制", + Finish: "完成", Focus: '聚焦', Support: '支持', Upload: '上传', diff --git a/packages/editor/src/store/modules/pathDrawing.ts b/packages/editor/src/store/modules/pathDrawing.ts new file mode 100644 index 0000000..479b388 --- /dev/null +++ b/packages/editor/src/store/modules/pathDrawing.ts @@ -0,0 +1,60 @@ +import { defineStore } from "pinia"; +import { store } from "@/store"; + +interface IPathDrawingState { + active: boolean; + template: Record | null; + submit: ((payload: IPathDrawingResult) => void) | null; +} + +export interface IPathDrawingResult { + worldPoints: Array<{ x: number; y: number; z: number }>; + origin: { x: number; y: number; z: number }; + options: Record; +} + +export interface IPathDrawingRequest { + template?: Record | null; + submit?: (payload: IPathDrawingResult) => void; +} + +function isPathDrawingRequest(payload: Record | IPathDrawingRequest) { + return Object.prototype.hasOwnProperty.call(payload, "template") || Object.prototype.hasOwnProperty.call(payload, "submit"); +} + +export const usePathDrawingStore = defineStore({ + id: "pathDrawing", + state: (): IPathDrawingState => ({ + active: false, + template: null, + submit: null, + }), + getters: { + isActive: state => state.active, + getTemplate: state => state.template, + getSubmit: state => state.submit, + }, + actions: { + start(payload: Record | IPathDrawingRequest) { + const request = isPathDrawingRequest(payload) ? payload : { template: payload }; + + this.template = request.template ? JSON.parse(JSON.stringify(request.template)) : null; + this.submit = typeof request.submit === "function" ? request.submit : null; + this.active = true; + }, + cancel() { + this.active = false; + this.template = null; + this.submit = null; + }, + finish() { + this.active = false; + this.template = null; + this.submit = null; + }, + }, +}); + +export function usePathDrawingStoreWithOut() { + return usePathDrawingStore(store); +} diff --git a/packages/editor/src/views/editor/components/extraPane/resource/builtin/Expansion.vue b/packages/editor/src/views/editor/components/extraPane/resource/builtin/Expansion.vue index e11b93d..2c66ab0 100644 --- a/packages/editor/src/views/editor/components/extraPane/resource/builtin/Expansion.vue +++ b/packages/editor/src/views/editor/components/extraPane/resource/builtin/Expansion.vue @@ -46,6 +46,7 @@ import type { Ref } from "vue"; import { Box3, Vector3, type Object3D } from "three"; import { cpt } from "@/language"; import { useDragStore } from "@/store/modules/drag"; +import { usePathDrawingStore } from "@/store/modules/pathDrawing"; import { screenToWorld } from "@/utils/common/scenes"; import { App, AddObjectCommand, Heatmap, Path, UIPanel, Water, type Preview } from "@astral3d/engine"; @@ -59,6 +60,7 @@ interface ExpansionItem { const searchText = inject("searchText") as Ref; const previewInfo = inject("previewInfo") as any; const previewRef = inject("previewRef") as any; +const pathDrawingStore = usePathDrawingStore(); const activeSubCategory = ref("heatmap"); const subCategories = ref([ @@ -514,6 +516,17 @@ function selectSubCategory(key: string) { activeSubCategory.value = key; } +function startPathDrawing(item: ExpansionItem) { + const options = JSON.parse(JSON.stringify(item.options || {})); + if (!options.name) { + options.name = getItemName(item); + } + if (pathDrawingStore.isActive) { + pathDrawingStore.cancel(); + } + pathDrawingStore.start(options); +} + function getDefaultAddPosition(): number[] | undefined { const container = window.viewer?.container; if (!container) return undefined; @@ -580,8 +593,7 @@ function addToScene(item: ExpansionItem, position?: number[]) { break; } case "path": { - const path = new Path(options); - App.execute(new AddObjectCommand(path), `Add Path: ${options.name}`); + startPathDrawing(item); break; } case "uipanel": { @@ -603,6 +615,12 @@ function dragStart(item: ExpansionItem) { function dragEnd() { if (dragStore.getActionTarget !== "addToScene" || dragStore.endArea !== "Scene") return; + if (activeSubCategory.value === "path") { + startPathDrawing(dragStore.getData); + dragStore.setActionTarget(""); + return; + } + const position = screenToWorld(dragStore.endPosition.x, dragStore.endPosition.y).toArray(); addToScene(dragStore.getData, position); diff --git a/packages/editor/src/views/editor/layouts/viewport/PathDrawingOverlay.vue b/packages/editor/src/views/editor/layouts/viewport/PathDrawingOverlay.vue new file mode 100644 index 0000000..b41cc8c --- /dev/null +++ b/packages/editor/src/views/editor/layouts/viewport/PathDrawingOverlay.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/packages/editor/src/views/editor/layouts/viewport/Viewport.vue b/packages/editor/src/views/editor/layouts/viewport/Viewport.vue index 75e874e..c5c3385 100644 --- a/packages/editor/src/views/editor/layouts/viewport/Viewport.vue +++ b/packages/editor/src/views/editor/layouts/viewport/Viewport.vue @@ -11,6 +11,8 @@ + + @@ -18,6 +20,7 @@ import {onMounted, ref, nextTick, onBeforeUnmount} from 'vue'; import {App,Viewer,Hooks} from "@astral3d/engine"; import Toolbar from "./Toolbar.vue"; +import PathDrawingOverlay from "./PathDrawingOverlay.vue"; import {useGlobalConfigStore} from "@/store/modules/globalConfig"; import {usePluginStore} from "@/store/modules/plugin"; import {installBuiltinPlugin} from "@/plugin";