From 7a06a506d2b65b8d39c563540b61bf0f8ea0489c Mon Sep 17 00:00:00 2001 From: yvan Date: Sat, 14 Jun 2025 00:19:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A6=E4=BE=A7=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +- pnpm-lock.yaml | 78 ++- src/components/YvJsonEditor.vue | 174 +++++ src/components/yvTable/DeleteCellRenderer.vue | 51 ++ src/components/yvTable/YvAggridCheckbox.vue | 259 ++++++++ src/components/yvTable/YvAggridCombo.vue | 154 +++++ src/components/yvTable/YvJsonCode.vue | 105 +++ src/components/yvTable/YvTable.vue | 808 ++++++++++++++++++++++++ src/components/yvTable/yv-aggrid-cn.locale.js | 69 ++ src/components/yvTable/yv-aggrid-en.locale.js | 69 ++ src/core/controls/DragControl.ts | 11 +- src/core/controls/IControls.ts | 7 - src/core/controls/MouseMoveInspect.ts | 3 +- src/core/controls/SelectInspect.ts | 3 +- src/core/engine/Viewport.ts | 43 +- src/core/manager/EntityManager.ts | 4 + src/core/manager/ItemFindManager.ts | 171 +---- src/editor/ModelMain.less | 4 + src/editor/widgets/modeltree/ModeltreeView.vue | 35 +- src/editor/widgets/modeltree/ModeltreeViewJs.js | 133 ++-- src/modules/gstore/GstoreRenderer.ts | 1 - src/types/Types.d.ts | 8 + src/utils/webutils.ts | 67 ++ 23 files changed, 1952 insertions(+), 309 deletions(-) create mode 100644 src/components/YvJsonEditor.vue create mode 100644 src/components/yvTable/DeleteCellRenderer.vue create mode 100644 src/components/yvTable/YvAggridCheckbox.vue create mode 100644 src/components/yvTable/YvAggridCombo.vue create mode 100644 src/components/yvTable/YvJsonCode.vue create mode 100644 src/components/yvTable/YvTable.vue create mode 100644 src/components/yvTable/yv-aggrid-cn.locale.js create mode 100644 src/components/yvTable/yv-aggrid-en.locale.js delete mode 100644 src/core/controls/IControls.ts diff --git a/package.json b/package.json index 3e88c00..67c44bb 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@types/lodash": "^4.17.7", "@types/node": "^22.14.0", "@types/three": "^0.176.0", + "@types/codemirror": "^5.60.16", "@vicons/antd": "^0.13.0", "@vicons/fa": "^0.12.0", "@vitejs/plugin-vue": "^5.2.3", @@ -52,7 +53,7 @@ "rimraf": "^6.0.1", "sortablejs": "1.15.6", "split.js": "^1.6.4", - "three": "^0.176.0", + "three": "^0.177.0", "troika-three-text": "^0.52.4", "tslib": "2.8.1", "typescript": "~5.8.0", @@ -63,6 +64,7 @@ "vue-router": "^4.5.0", "vue-tsc": "^2.2.8", "vue3-menus": "^1.1.2", + "three-mesh-bvh": "^0.9.0", "three-dxf-viewer": "^1.0.36" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffb118b..af07000 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,6 +27,9 @@ importers: '@tsconfig/node22': specifier: ^22.0.1 version: 22.0.2 + '@types/codemirror': + specifier: ^5.60.16 + version: 5.60.16 '@types/jquery': specifier: ^3.3.31 version: 3.5.32 @@ -68,7 +71,7 @@ importers: version: 1.9.0 camera-controls: specifier: 2.10.1 - version: 2.10.1(three@0.176.0) + version: 2.10.1(three@0.177.0) codemirror: specifier: ^5.65.19 version: 5.65.19 @@ -121,14 +124,17 @@ importers: specifier: ^1.6.4 version: 1.6.5 three: - specifier: ^0.176.0 - version: 0.176.0 + specifier: ^0.177.0 + version: 0.177.0 three-dxf-viewer: specifier: ^1.0.36 version: 1.0.36 + three-mesh-bvh: + specifier: ^0.9.0 + version: 0.9.0(three@0.177.0) troika-three-text: specifier: ^0.52.4 - version: 0.52.4(three@0.176.0) + version: 0.52.4(three@0.177.0) tslib: specifier: 2.8.1 version: 2.8.1 @@ -570,56 +576,67 @@ packages: resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.41.0': resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.41.0': resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.41.0': resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.41.0': resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.41.0': resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.41.0': resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.41.0': resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.41.0': resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.41.0': resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.41.0': resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==} @@ -652,6 +669,9 @@ packages: '@tweenjs/tween.js@23.1.3': resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@types/codemirror@5.60.16': + resolution: {integrity: sha512-V/yHdamffSS075jit+fDxaOAmdP2liok8NSNJnAZfDJErzOheuygHZEhAJrfmk5TEyM32MhkZjwo/idX791yxw==} + '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} @@ -673,6 +693,9 @@ packages: '@types/stats.js@0.17.4': resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + '@types/tern@0.23.9': + resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} + '@types/three@0.176.0': resolution: {integrity: sha512-FwfPXxCqOtP7EdYMagCFePNKoG1AGBDUEVKtluv2BTVRpSt7b+X27xNsirPCTCqY1pGYsPUzaM3jgWP7dXSxlw==} @@ -941,7 +964,7 @@ packages: engines: {node: '>= 0.8'} commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, tarball: https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz} + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1002,7 +1025,7 @@ packages: engines: {node: '>= 0.4'} dxf@5.2.0: - resolution: {integrity: sha512-qk29/318lCOrDWKPEx8MztNjBSGoXoPlpyYkgRwyAKMb8UEZ5nQU5IU3ruAYOtSgCWjiVZng41ros4CNkLuMiQ==, tarball: https://registry.npmmirror.com/dxf/-/dxf-5.2.0.tgz} + resolution: {integrity: sha512-qk29/318lCOrDWKPEx8MztNjBSGoXoPlpyYkgRwyAKMb8UEZ5nQU5IU3ruAYOtSgCWjiVZng41ros4CNkLuMiQ==} engines: {node: '>=8.9.0'} hasBin: true @@ -1590,13 +1613,18 @@ packages: engines: {node: '>=16'} three-dxf-viewer@1.0.36: - resolution: {integrity: sha512-Tu7k+/yCyovpMJTeQ8fpGHdjtynq66vMMXYzDCwI9prJo2dRDxig4S+ixQVuMIy3I99VZa51z5KwlkeWyCnFzQ==, tarball: https://registry.npmmirror.com/three-dxf-viewer/-/three-dxf-viewer-1.0.36.tgz} + resolution: {integrity: sha512-Tu7k+/yCyovpMJTeQ8fpGHdjtynq66vMMXYzDCwI9prJo2dRDxig4S+ixQVuMIy3I99VZa51z5KwlkeWyCnFzQ==} + + three-mesh-bvh@0.9.0: + resolution: {integrity: sha512-xAwZj0hZknpwVsdK5BBJTIAZDjDPZCRzURY1o+z/JHBON/jc2UetK1CzPeQZiiOVSfI4jV2z7sXnnGtgsgnjaA==} + peerDependencies: + three: '>= 0.159.0' three@0.171.0: - resolution: {integrity: sha512-Y/lAXPaKZPcEdkKjh0JOAHVv8OOnv/NDJqm0wjfCzyQmfKxV7zvkwsnBgPBKTzJHToSOhRGQAGbPJObT59B/PQ==, tarball: https://registry.npmmirror.com/three/-/three-0.171.0.tgz} + resolution: {integrity: sha512-Y/lAXPaKZPcEdkKjh0JOAHVv8OOnv/NDJqm0wjfCzyQmfKxV7zvkwsnBgPBKTzJHToSOhRGQAGbPJObT59B/PQ==} - three@0.176.0: - resolution: {integrity: sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==} + three@0.177.0: + resolution: {integrity: sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==} tinyglobby@0.2.13: resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} @@ -1645,7 +1673,7 @@ packages: browserslist: '>= 4.21.0' vecks@3.9.2: - resolution: {integrity: sha512-ubOo1KoOrPu3llYmlE4SH60JWcR+YDaObP+1Kzpbcl09dKIf0fbBweW8Xyg+6Nk2Rrnyit6Jxw9bx2LOYpMQMA==, tarball: https://registry.npmmirror.com/vecks/-/vecks-3.9.2.tgz} + resolution: {integrity: sha512-ubOo1KoOrPu3llYmlE4SH60JWcR+YDaObP+1Kzpbcl09dKIf0fbBweW8Xyg+6Nk2Rrnyit6Jxw9bx2LOYpMQMA==} vite-hot-client@2.0.4: resolution: {integrity: sha512-W9LOGAyGMrbGArYJN4LBCdOC5+Zwh7dHvOHC0KmGKkJhsOzaKbpo/jEjpPKVHIW0/jBWj8RZG0NUxfgA8BxgAg==} @@ -2239,6 +2267,10 @@ snapshots: '@tweenjs/tween.js@23.1.3': {} + '@types/codemirror@5.60.16': + dependencies: + '@types/tern': 0.23.9 + '@types/estree@1.0.7': {} '@types/jquery@3.5.32': @@ -2259,6 +2291,10 @@ snapshots: '@types/stats.js@0.17.4': {} + '@types/tern@0.23.9': + dependencies: + '@types/estree': 1.0.7 + '@types/three@0.176.0': dependencies: '@dimforge/rapier3d-compat': 0.12.0 @@ -2601,9 +2637,9 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - camera-controls@2.10.1(three@0.176.0): + camera-controls@2.10.1(three@0.177.0): dependencies: - three: 0.176.0 + three: 0.177.0 caniuse-lite@1.0.30001718: {} @@ -3249,9 +3285,13 @@ snapshots: dxf: 5.2.0 three: 0.171.0 + three-mesh-bvh@0.9.0(three@0.177.0): + dependencies: + three: 0.177.0 + three@0.171.0: {} - three@0.176.0: {} + three@0.177.0: {} tinyglobby@0.2.13: dependencies: @@ -3260,17 +3300,17 @@ snapshots: totalist@3.0.1: {} - troika-three-text@0.52.4(three@0.176.0): + troika-three-text@0.52.4(three@0.177.0): dependencies: bidi-js: 1.0.3 - three: 0.176.0 - troika-three-utils: 0.52.4(three@0.176.0) + three: 0.177.0 + troika-three-utils: 0.52.4(three@0.177.0) troika-worker-utils: 0.52.0 webgl-sdf-generator: 1.1.1 - troika-three-utils@0.52.4(three@0.176.0): + troika-three-utils@0.52.4(three@0.177.0): dependencies: - three: 0.176.0 + three: 0.177.0 troika-worker-utils@0.52.0: {} diff --git a/src/components/YvJsonEditor.vue b/src/components/YvJsonEditor.vue new file mode 100644 index 0000000..5ad3444 --- /dev/null +++ b/src/components/YvJsonEditor.vue @@ -0,0 +1,174 @@ + + + \ No newline at end of file diff --git a/src/components/yvTable/DeleteCellRenderer.vue b/src/components/yvTable/DeleteCellRenderer.vue new file mode 100644 index 0000000..d993548 --- /dev/null +++ b/src/components/yvTable/DeleteCellRenderer.vue @@ -0,0 +1,51 @@ + + \ No newline at end of file diff --git a/src/components/yvTable/YvAggridCheckbox.vue b/src/components/yvTable/YvAggridCheckbox.vue new file mode 100644 index 0000000..3dd120f --- /dev/null +++ b/src/components/yvTable/YvAggridCheckbox.vue @@ -0,0 +1,259 @@ + + diff --git a/src/components/yvTable/YvAggridCombo.vue b/src/components/yvTable/YvAggridCombo.vue new file mode 100644 index 0000000..ebe15f2 --- /dev/null +++ b/src/components/yvTable/YvAggridCombo.vue @@ -0,0 +1,154 @@ + + + \ No newline at end of file diff --git a/src/components/yvTable/YvJsonCode.vue b/src/components/yvTable/YvJsonCode.vue new file mode 100644 index 0000000..f2286a1 --- /dev/null +++ b/src/components/yvTable/YvJsonCode.vue @@ -0,0 +1,105 @@ + + diff --git a/src/components/yvTable/YvTable.vue b/src/components/yvTable/YvTable.vue new file mode 100644 index 0000000..9078346 --- /dev/null +++ b/src/components/yvTable/YvTable.vue @@ -0,0 +1,808 @@ + + + diff --git a/src/components/yvTable/yv-aggrid-cn.locale.js b/src/components/yvTable/yv-aggrid-cn.locale.js new file mode 100644 index 0000000..95d044e --- /dev/null +++ b/src/components/yvTable/yv-aggrid-cn.locale.js @@ -0,0 +1,69 @@ +export const localeText = { + page: "页", + more: "更多", + to: "到", + of: "of", + next: "下⼀页", + last: "上⼀页", + first: "⾸页", + previous: "上⼀页", + loadingOoo: "加载中...", + selectAll: "查询全部", + searchOoo: "查询...", + blanks: "空⽩", + filterOoo: "过滤...", + applyFilter: "保存过滤器...", + equals: "相等", + notEqual: "不相等", + lessThan: "⼩于", + greaterThan: "⼤于", + lessThanOrEqual: "⼩于等于", + greaterThanOrEqual: "⼤于等于", + inRange: "范围", + contains: "包含", + notContains: "不包含", + startsWith: "开始于", + endsWith: "结束于", + group: "组", + columns: "列", + filters: "筛选", + rowGroupColumns: "laPivot Cols", + rowGroupColumnsEmptyMessage: "la drag cols to group", + valueColumns: "laValue Cols", + pivotMode: "laPivot-Mode", + groups: "laGroups", + values: "值", + pivots: "laPivots", + valueColumnsEmptyMessage: "la drag cols to aggregate", + pivotColumnsEmptyMessage: "la drag here to pivot", + toolPanelButton: "la tool panel", + noRowsToShow: "数据为空", + pinColumn: "固定", + valueAggregation: "laValue Agg", + autosizeThiscolumn: "自动调整宽度", + autosizeAllColumns: "自动调整所有字段宽度", + groupBy: "分组", + ungroupBy: "不分组", + resetColumns: "重置列", + expandAll: "展开全部", + collapseAll: "关闭", + toolPanel: "⼯具⾯板", + export: "导出", + csvExport: "导出为CSV格式⽂件", + excelExport: "导出到Excel", + pinLeft: "左固定 <<", + pinRight: "右固定 >>", + noPin: "不要固定", + sum: "总数", + min: "最⼩值", + max: "最⼤值", + none: "⽆", + count: "总", + average: "平均值", + copy: "复制", + copyWithHeaders: "带表头复制", + copyWithGroupHeaders: '带分组表头复制', + ctrlC: "ctrl + C", + paste: "粘贴", + ctrlV: "ctrl + V" +} \ No newline at end of file diff --git a/src/components/yvTable/yv-aggrid-en.locale.js b/src/components/yvTable/yv-aggrid-en.locale.js new file mode 100644 index 0000000..95d044e --- /dev/null +++ b/src/components/yvTable/yv-aggrid-en.locale.js @@ -0,0 +1,69 @@ +export const localeText = { + page: "页", + more: "更多", + to: "到", + of: "of", + next: "下⼀页", + last: "上⼀页", + first: "⾸页", + previous: "上⼀页", + loadingOoo: "加载中...", + selectAll: "查询全部", + searchOoo: "查询...", + blanks: "空⽩", + filterOoo: "过滤...", + applyFilter: "保存过滤器...", + equals: "相等", + notEqual: "不相等", + lessThan: "⼩于", + greaterThan: "⼤于", + lessThanOrEqual: "⼩于等于", + greaterThanOrEqual: "⼤于等于", + inRange: "范围", + contains: "包含", + notContains: "不包含", + startsWith: "开始于", + endsWith: "结束于", + group: "组", + columns: "列", + filters: "筛选", + rowGroupColumns: "laPivot Cols", + rowGroupColumnsEmptyMessage: "la drag cols to group", + valueColumns: "laValue Cols", + pivotMode: "laPivot-Mode", + groups: "laGroups", + values: "值", + pivots: "laPivots", + valueColumnsEmptyMessage: "la drag cols to aggregate", + pivotColumnsEmptyMessage: "la drag here to pivot", + toolPanelButton: "la tool panel", + noRowsToShow: "数据为空", + pinColumn: "固定", + valueAggregation: "laValue Agg", + autosizeThiscolumn: "自动调整宽度", + autosizeAllColumns: "自动调整所有字段宽度", + groupBy: "分组", + ungroupBy: "不分组", + resetColumns: "重置列", + expandAll: "展开全部", + collapseAll: "关闭", + toolPanel: "⼯具⾯板", + export: "导出", + csvExport: "导出为CSV格式⽂件", + excelExport: "导出到Excel", + pinLeft: "左固定 <<", + pinRight: "右固定 >>", + noPin: "不要固定", + sum: "总数", + min: "最⼩值", + max: "最⼤值", + none: "⽆", + count: "总", + average: "平均值", + copy: "复制", + copyWithHeaders: "带表头复制", + copyWithGroupHeaders: '带分组表头复制', + ctrlC: "ctrl + C", + paste: "粘贴", + ctrlV: "ctrl + V" +} \ No newline at end of file diff --git a/src/core/controls/DragControl.ts b/src/core/controls/DragControl.ts index 7f4f855..6c6a770 100644 --- a/src/core/controls/DragControl.ts +++ b/src/core/controls/DragControl.ts @@ -1,6 +1,5 @@ import * as THREE from 'three' import type Viewport from '@/core/engine/Viewport.ts' -import type IControls from '@/core/controls/IControls.ts' import { getClosestObject } from '@/core/ModelUtils.ts' import EventBus from '@/runtime/EventBus.ts' import type { Object3DLike } from '@/types/ModelTypes.ts' @@ -10,7 +9,7 @@ import { LineManageWrap } from '@/core/manager/LineSegmentManager.ts' /** * ThreeJS 拖拽管理器(仅限 X/Z 平面) */ -export default class DragControl implements IControls { +export default class DragControl { private viewport: Viewport private _is_enabled: boolean = true private domElement: HTMLElement @@ -109,11 +108,11 @@ export default class DragControl implements IControls { } else { // 射线方法修改 ========================== - // const mouse = this.getMousePosition(event.clientX, event.clientY) - // const intersected = this.getIntersectedDraggableObject(mouse) + const mouse = this.getMousePosition(event.clientX, event.clientY) + const intersected = this.getIntersectedDraggableObject(mouse) // ===================================== - const ids = this.viewport.itemFindManager.getItemsByPosition(CurrentMouseInfo.x, CurrentMouseInfo.z) - this.domElement.style.cursor = ids.length > 0 ? 'grab' : 'auto' + // const ids = this.viewport.itemFindManager.getItemsByPosition(CurrentMouseInfo.x, CurrentMouseInfo.z) + this.domElement.style.cursor = intersected ? 'grab' : 'auto' } } diff --git a/src/core/controls/IControls.ts b/src/core/controls/IControls.ts deleted file mode 100644 index ab341c7..0000000 --- a/src/core/controls/IControls.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface IControls { - init(viewport: any): void - - dispose(): void - - animate?: () => void; -} \ No newline at end of file diff --git a/src/core/controls/MouseMoveInspect.ts b/src/core/controls/MouseMoveInspect.ts index f2d4f3b..7d23544 100644 --- a/src/core/controls/MouseMoveInspect.ts +++ b/src/core/controls/MouseMoveInspect.ts @@ -1,5 +1,4 @@ import type Viewport from '@/core/engine/Viewport' -import type IControls from './IControls' import * as THREE from 'three' let pmFn, otFn, lvFn @@ -7,7 +6,7 @@ let pmFn, otFn, lvFn /** * 鼠标移动时,将鼠标位置的坐标转换为设计图上的坐标,并设置到 designer.mousePos 属性中 */ -export default class MouseMoveInspect implements IControls { +export default class MouseMoveInspect { viewport: Viewport canvas: HTMLCanvasElement diff --git a/src/core/controls/SelectInspect.ts b/src/core/controls/SelectInspect.ts index f075275..e71bb78 100644 --- a/src/core/controls/SelectInspect.ts +++ b/src/core/controls/SelectInspect.ts @@ -1,5 +1,4 @@ import * as THREE from 'three' -import type IControls from './IControls' import type Viewport from '@/core/engine/Viewport' import { Line2 } from 'three/examples/jsm/lines/Line2.js' import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js' @@ -15,7 +14,7 @@ import { getAABBox, getOBBox } from '@/core/ModelUtils.ts' /** * 选择工具,用于在设计器中显示选中对象的包围盒 */ -export default class SelectInspect implements IControls { +export default class SelectInspect { viewport: Viewport /** * 线框材质,用于显示选中对象的包围盒 diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index 3bebf71..1695620 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -5,7 +5,6 @@ import Stats from 'three/examples/jsm/libs/stats.module' import type WorldModel from '../manager/WorldModel' import $ from 'jquery' import { markRaw, reactive, toRaw, watch } from 'vue' -import type IControls from '../controls/IControls' import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' @@ -41,29 +40,28 @@ export default class Viewport { // dragControl: any // EsDragControls animationFrameId: any = null scene: SceneHelp + selectInspect = new SelectInspect() mouseMoveInspect = new MouseMoveInspect() dragControl = new DragControl() labelManager = new LabelManager() + entityManager = new EntityManager() + itemFindManager = new ItemFindManager() + interactionManager = new InteractionManager() + + // 状态管理器 + stateManager: StateManager tools: IControls[] = [ markRaw(this.selectInspect), markRaw(this.mouseMoveInspect), markRaw(this.dragControl), - markRaw(this.labelManager) + markRaw(this.labelManager), + markRaw(this.entityManager), + markRaw(this.itemFindManager), + markRaw(this.interactionManager) ] - // 状态管理器 - stateManager: StateManager - - // 实体管理器 - entityManager = new EntityManager() - - itemFindManager = new ItemFindManager() - - // 交互管理器 - interactionManager = new InteractionManager() - // 点实例管理器 moduleName -> InstancePointManager pointManagerMap: Map = new Map() @@ -168,9 +166,7 @@ export default class Viewport { console.log('viewport on catelogCode: ' + this.scene.catalogCode) const viewerDom = this.viewerDom - // 初始化各种管理器 - this.entityManager.init(this) - this.interactionManager.init(this) + // 管理器 this.stateManager = new StateManager(option.stateManagerId, this) // 渲染器 @@ -440,6 +436,21 @@ export default class Viewport { // return viewHeight / (2 * Math.tan(THREE.MathUtils.degToRad(referenceFOV) / 2)) // } + cameraToEntity(id: string) { + const { tf } = this.entityManager.findItemById(id) + // 移动正交相机去往目标点 + if (this.camera instanceof THREE.OrthographicCamera) { + this.camera.position.set(tf[0][0], 60, tf[0][2]) + this.camera.lookAt(tf[0][0], 0, tf[0][2]) + this.camera.zoom = 60 + this.camera.updateProjectionMatrix() + + } else if (this.camera instanceof THREE.PerspectiveCamera) { + this.camera.position.set(tf[0][0], tf[1][1] + 10, tf[2][2]) + this.camera.lookAt(tf[0][0], tf[1][1], tf[2][2]) + } + } + handleResize(entries: any) { for (let entry of entries) { // entry.contentRect包含了元素的尺寸信息 diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index 0a9d1fa..8c5da48 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -47,6 +47,10 @@ export default class EntityManager { private readonly writeBackEntities = new Set() isUpdating = false + getAllEntityForGrid() { + return Array.from(this.___entityMap.values()) + } + dispose() { // 清理所有差量渲染器 for (const renderer of this.diffRenderer.values()) { diff --git a/src/core/manager/ItemFindManager.ts b/src/core/manager/ItemFindManager.ts index 1ff27f8..5390e58 100644 --- a/src/core/manager/ItemFindManager.ts +++ b/src/core/manager/ItemFindManager.ts @@ -1,200 +1,45 @@ import * as THREE from 'three' -import rbush from 'rbush' import { OBB } from 'three/examples/jsm/math/OBB' +import type Viewport from '@/core/engine/Viewport.ts' // import { Octree } from 'three/examples/jsm/math/Octree.js' // import { QuadTreeNode } from '@/core/QuadTree.ts' // import { convexHull } from '@/core/ModelUtils.ts' -interface ItemEntry extends rbush.BBox { - id: string - obb: OBB -} // 主管理器类 export default class ItemFindManager { - private spatialIndex = new rbush() - private items = new Map() + private items = new Map() dispose() { - this.spatialIndex.clear() this.items.clear() } constructor() { } + init(viewport: Viewport) { + } + // 添加或更新物品 addOrUpdate(...items: ItemMetrix[]): void { - for (const item of items) { - const aabb = itemToAABB(item) - const obb = itemToOBB(item) - - if (this.items.has(item.id)) { - this.remove(item.id) - } - - const entry: ItemEntry = { - id: item.id, - obb, - ...aabb - } - - this.items.set(item.id, entry) - this.spatialIndex.insert(entry) - } } // 移除物品 remove(...ids: string[]): void { - for (const id of ids) { - const entry = this.items.get(id) - if (entry) { - this.spatialIndex.remove(entry) - this.items.delete(id) - } - } } // 位置查询 getItemsByPosition(x: number, z: number): string[] { - const candidates = this.spatialIndex.search({ - minX: x, - minY: z, - maxX: x, - maxY: z - }) - - const point = new THREE.Vector3(x, 0, z) - return candidates.filter((item) => pointIntersectsOBB(point, item.obb)).map((item) => item.id) + return [] } // 距离查询 getItemsByDistance(x: number, z: number, distance: number): string[] { - const sphere = new THREE.Sphere(new THREE.Vector3(x, 0, z), distance) - - const candidates = this.spatialIndex.search({ - minX: x - distance, - minY: z - distance, - maxX: x + distance, - maxY: z + distance - }) - - return candidates.filter((item) => sphereIntersectsOBB(sphere, item.obb)).map((item) => item.id) + return [] } // 矩形区域查询(有交集) getItemsByRect(x1: number, z1: number, x2: number, z2: number): string[] { - const rect = { - minX: Math.min(x1, x2), - maxX: Math.max(x1, x2), - minY: Math.min(z1, z2), - maxY: Math.max(z1, z2) - } - - const candidates = this.spatialIndex.search(rect) - return candidates.filter((item) => rectIntersectsOBB(rect, item.obb)).map((item) => item.id) + return [] } } - -function pointIntersectsOBB(point: THREE.Vector3, obb: OBB): boolean { - const box = new THREE.Box3() - setBox3FromOBB(box, obb) - return box.containsPoint(point) -} - -function sphereIntersectsOBB(sphere: THREE.Sphere, obb: OBB): boolean { - const box = new THREE.Box3() - setBox3FromOBB(box, obb) - return box.intersectsSphere(sphere) -} - -function rectIntersectsOBB(rect: { minX: number; minY: number; maxX: number; maxY: number }, obb: OBB): boolean { - // 简化判断:用 AABB 投影到 XZ 平面做矩形交叉检测 - const aabb = new THREE.Box3() - setBox3FromOBB(aabb, obb) - const aabb2D = { - minX: aabb.min.x, - minY: aabb.min.z, - maxX: aabb.max.x, - maxY: aabb.max.z - } - - return !( - rect.maxX < aabb2D.minX || - rect.minX > aabb2D.maxX || - rect.maxY < aabb2D.minY || - rect.minY > aabb2D.maxY - ) -} - -export function itemToAABB(item: ItemMetrix): { minX: number; minY: number; maxX: number; maxY: number } { - // 假设所有物品都是立方体,尺寸为 scale.x × scale.z - const x = item.tf[0][0] - const z = item.tf[0][2] - const halfWidth = item.tf[2][0] / 2 - const halfDepth = item.tf[2][2] / 2 - - return { - minX: x - halfWidth, - maxX: x + halfWidth, - minY: z - halfDepth, - maxY: z + halfDepth - } -} - -export function itemToOBB(item: ItemMetrix): OBB { - const position = new THREE.Vector3(...item.tf[0]) - const rotation = new THREE.Euler( - THREE.MathUtils.degToRad(item.tf[1][0]), - THREE.MathUtils.degToRad(item.tf[1][1]), - THREE.MathUtils.degToRad(item.tf[1][2]), - 'XYZ' - ) - const scale = new THREE.Vector3(...item.tf[2]) - - const matrix = new THREE.Matrix4() - .makeRotationFromEuler(rotation) - .premultiply(new THREE.Matrix4().makeTranslation(position.x, position.y, position.z)) - .premultiply(new THREE.Matrix4().makeScale(scale.x, scale.y, scale.z)) - - const obb = new OBB( - new THREE.Vector3(), - new THREE.Vector3(0.5, 0.5, 0.5), - new THREE.Matrix3().setFromMatrix4(matrix) - ) - - return obb -} - -function setBox3FromOBB(box: THREE.Box3, obb: OBB): THREE.Box3 { - const center = obb.center - const halfSize = new THREE.Vector3().copy(obb.halfSize) - const rotation = obb.rotation - - // 8 个局部顶点 - const vertices = [ - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, 1, 1)), - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, 1, 1)), - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, -1, 1)), - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, -1, 1)), - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, 1, -1)), - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, 1, -1)), - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(-1, -1, -1)), - new THREE.Vector3().copy(halfSize).multiply(new THREE.Vector3(1, -1, -1)) - ] - - // 应用旋转和平移到每个顶点 - const worldVertices = vertices.map((v) => { - return v.applyMatrix3(rotation).add(center) - }) - - // 构造包围盒 - box.min.set(Infinity, Infinity, Infinity) - box.max.set(-Infinity, -Infinity, -Infinity) - - for (const v of worldVertices) { - box.expandByPoint(v) - } - - return box -} diff --git a/src/editor/ModelMain.less b/src/editor/ModelMain.less index 76e0280..57ad390 100644 --- a/src/editor/ModelMain.less +++ b/src/editor/ModelMain.less @@ -172,6 +172,7 @@ .calc-left-panel { flex: 1; overflow: auto; + display: flex; } .calc-right-panel { @@ -279,6 +280,7 @@ display: flex; align-items: center; flex-direction: row; + .el-button { margin-left: 5px; } @@ -289,9 +291,11 @@ background: #dcdcdc; margin: 0 5px; } + .el-button-group { flex: 1; } + &.section-bottom-toolbar { justify-content: space-between; diff --git a/src/editor/widgets/modeltree/ModeltreeView.vue b/src/editor/widgets/modeltree/ModeltreeView.vue index a84fb5b..3695c89 100644 --- a/src/editor/widgets/modeltree/ModeltreeView.vue +++ b/src/editor/widgets/modeltree/ModeltreeView.vue @@ -1,30 +1,31 @@ \ No newline at end of file + + diff --git a/src/editor/widgets/modeltree/ModeltreeViewJs.js b/src/editor/widgets/modeltree/ModeltreeViewJs.js index 0734559..611941f 100644 --- a/src/editor/widgets/modeltree/ModeltreeViewJs.js +++ b/src/editor/widgets/modeltree/ModeltreeViewJs.js @@ -1,35 +1,42 @@ -import { defineComponent } from 'vue' -import { renderIcon } from '@/utils/webutils.js' +import { defineComponent, markRaw } from 'vue' +import YvTable from '@/components/yvTable/YvTable.vue' import IWidgets from '../IWidgets.js' - export default defineComponent({ name: 'ModeltreeView', + components: { YvTable }, mixins: [IWidgets], data() { + return { - searchKeyword: '', - treedata: data + columnSetting: Object.freeze(GRID1_SETTING), + grid1Data: [], + searchKeyword: '' } }, + mounted() { + }, methods: { - allowDrop(event) { - return true - }, - allowDrag(event) { - return true - }, - handleDragStart() { - }, - handleDragEnter() { - }, - handleDragLeave() { - }, - handleDragOver() { + refreshGrid1() { + if (this.viewport?.entityManager) { + this.grid1Data = Object.freeze(this.viewport.entityManager.getAllEntityForGrid()) + } else { + this.grid1Data = [] + } }, - handleDragEnd() { + onGrid1Ready() { + this.refreshGrid1() }, - handleDrop() { + grid1RowClick(evt) { + console.log(this.$refs.grid1, evt) + const { data } = evt + if (data?.id) { + this.viewport.selectInspect.selectById(data.id) + this.viewport.cameraToEntity(data.id) + + } else { + system.msg('点位没找到:' + data?.id, 'error') + } } }, computed: { @@ -57,60 +64,36 @@ export default defineComponent({ } }) -const data = [ - { - label: 'Level one 1', - children: [ - { - label: 'Level two 1-1', - children: [ - { - label: 'Level three 1-1-1' - } - ] +const GRID1_SETTING = { + editable: true, + sortable: false, + serverSortable: false, + rowNumber: false, + disableColumnMenu: false, + mode: 'edit2', + rowHeight: 20, + headerHeight: 30, + showToolbar: true, + disableAppendButton: true, + disableDeleteButton: true, + // domLayout: 'autoHeight', + columns: [ + { dataIndex: 't', width: 50, header: 't', editable: false, flex: 1 }, + { dataIndex: 'id', width: 50, header: 'id', editable: false, flex: 1 }, + { + dataIndex: 'pos', width: 50, header: '', editable: false, hidden: true, + valueGetter(evt) { + const item = evt.data + return _.get(item, 'tf[0][0]') + ',' + _.get(item, 'tf[0][1]') + ',' + _.get(item, 'tf[0][2]') } - ] - }, - { - label: 'Level one 2', - children: [ - { - label: 'Level two 2-1', - children: [ - { - label: 'Level three 2-1-1' - } - ] - }, - { - label: 'Level two 2-2', - children: [ - { - label: 'Level three 2-2-1' - } - ] - } - ] - }, - { - label: 'Level one 3', - children: [ - { - label: 'Level two 3-1', - children: [ - { - label: 'Level three 3-1-1' - } - ] - }, - { - label: 'Level two 3-2', - children: [ - { - label: 'Level three 3-2-1' - } - ] + }, + { + dataIndex: 'size', width: 50, header: '', editable: false, hidden: true, + valueGetter(evt) { + const item = evt.data + return _.get(item, 'tf[2][0]') + ',' + _.get(item, 'tf[2][1]') + ',' + _.get(item, 'tf[2][2]') } - ] - } -] \ No newline at end of file + } + ] +} + diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index fe907a6..c147caf 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -5,7 +5,6 @@ import { type Object3DLike } from '@/types/ModelTypes.ts' import InstancePointManager from '@/core/manager/InstancePointManager.ts' import LineSegmentManager from '@/core/manager/LineSegmentManager.ts' import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial' -import { itemToOBB } from '@/core/manager/ItemFindManager.ts' import { getOBBox } from '@/core/ModelUtils.ts' /** diff --git a/src/types/Types.d.ts b/src/types/Types.d.ts index 7b91269..4eb456d 100644 --- a/src/types/Types.d.ts +++ b/src/types/Types.d.ts @@ -63,3 +63,11 @@ interface CatalogGroup { * 世界模型目录数据 */ type Catalog = CatalogGroup[]; + +interface IControls { + init(viewport: any): void + + dispose(): void + + animate?: () => void; +} diff --git a/src/utils/webutils.ts b/src/utils/webutils.ts index e0a9124..e970d64 100644 --- a/src/utils/webutils.ts +++ b/src/utils/webutils.ts @@ -234,3 +234,70 @@ export function renderIcon(icon: string, props = {}): any { } return () => h(ElIcon, props, { default: () => h(component) }) } + + +/** + * 运行事件 + * @param scope vcxt.scope + * @param vjson VJSON,是指能包含 listeners 属性的对象 + * @param eventName 事件名 + * @param args 事件参数 + */ +export function runEvent(scope, vjson, eventName, ...args) { + if (!vjson.listeners) { + return + } + if (typeof vjson.listeners[eventName] === 'string') { + let funcBody = vjson.listeners[eventName] + const me = scope + const methodVarName = [] + + // // 吧 args 参数添加进去 + _.forEach(args, (v, k) => { + methodVarName.push('arg' + k) + }) + + // 花括号定义模式 + if (_.startsWith(funcBody, '{')) { + if (_.endsWith(funcBody, '}')) { + // 剔除花括号 + const methodName = funcBody.substring(1, funcBody.length - 1) + + // 检查有没有其他符号 + if (methodName.includes('+') || methodName.includes('-') || methodName.includes('(') || + methodName.includes(')') || methodName.includes('*') || methodName.includes('/') || + methodName.includes('{') || methodName.includes('}') || methodName.includes(' ')) { + console.error('错误表达式:' + funcBody) + return + } + // funcBody = "return this." + funcBody + "(arguments);" + try { + return me[methodName].call(me, ...args) + } catch (e) { + console.error(`页面方法(methodName=${methodName})运行错误`, '页面对象', me, '页面方法对象', me[methodName], '错误', e) + system.showErrorDialog('方法运行错误' + e) + return + } + + } else { + console.error('错误表达式:' + funcBody) + return + } + } + + const func = Function(...methodVarName, funcBody) + try { + return func.bind(scope)(...args) + } catch (e) { + console.error('runEventError', e) + //@ts-ignore + system.showErrorDialog(e.toString()) + return + } + } + if (typeof vjson.listeners[eventName] === 'function') { + //@ts-ignore + return vjson.listeners[eventName].call(scope, this, ...args)?.catch?.(() => { + }) + } +}