gltf-parser-plugin 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { Mesh, Vector3, Triangle, BufferGeometry, EventDispatcher, DataTexture, RGBAFormat, UnsignedByteType, SRGBColorSpace, Texture, MeshStandardMaterial, DoubleSide, FrontSide, BufferAttribute, OrthographicCamera, Float32BufferAttribute, Vector2, WebGLRenderer, WebGLRenderTarget, ShaderMaterial, OneFactor, ZeroFactor, CustomBlending, Box2, Matrix4, Matrix3, Matrix2, Vector4, MeshBasicMaterial, Loader, Scene, Group, InstancedMesh, Quaternion, Box3 } from "three";
1
+ import { Mesh, Vector3, Triangle, BufferGeometry, EventDispatcher, DataTexture, RGBAFormat, UnsignedByteType, SRGBColorSpace, Texture, MeshStandardMaterial, DoubleSide, FrontSide, BufferAttribute, Box3, Vector2, OrthographicCamera, Float32BufferAttribute, WebGLRenderer, WebGLRenderTarget, ShaderMaterial, OneFactor, ZeroFactor, CustomBlending, Box2, Matrix4, Matrix3, Matrix2, Vector4, MeshBasicMaterial, Loader, Scene, Group, InstancedMesh, Quaternion, Color } from "three";
2
2
  class FeatureIdUniforms {
3
3
  mesh;
4
4
  plugin;
@@ -554,6 +554,114 @@ function acquireWorker() {
554
554
  currentWorkerIndex = (currentWorkerIndex + 1) % workerPool.length;
555
555
  return worker;
556
556
  }
557
+ function pointInPolygon(px, py, polygon) {
558
+ let inside = false;
559
+ const n = polygon.length;
560
+ for (let i = 0, j = n - 1; i < n; j = i++) {
561
+ const xi = polygon[i].x, yi = polygon[i].y;
562
+ const xj = polygon[j].x, yj = polygon[j].y;
563
+ if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi) {
564
+ inside = !inside;
565
+ }
566
+ }
567
+ return inside;
568
+ }
569
+ function segmentsIntersect(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) {
570
+ const cross = (ox, oy, ax, ay, bx, by) => (ax - ox) * (by - oy) - (ay - oy) * (bx - ox);
571
+ const d1 = cross(bx1, by1, bx2, by2, ax1, ay1);
572
+ const d2 = cross(bx1, by1, bx2, by2, ax2, ay2);
573
+ const d3 = cross(ax1, ay1, ax2, ay2, bx1, by1);
574
+ const d4 = cross(ax1, ay1, ax2, ay2, bx2, by2);
575
+ if ((d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0)) {
576
+ return true;
577
+ }
578
+ const onSeg = (px, py, qx, qy, rx, ry) => Math.min(px, qx) <= rx && rx <= Math.max(px, qx) && Math.min(py, qy) <= ry && ry <= Math.max(py, qy);
579
+ if (d1 === 0 && onSeg(bx1, by1, bx2, by2, ax1, ay1)) return true;
580
+ if (d2 === 0 && onSeg(bx1, by1, bx2, by2, ax2, ay2)) return true;
581
+ if (d3 === 0 && onSeg(ax1, ay1, ax2, ay2, bx1, by1)) return true;
582
+ if (d4 === 0 && onSeg(ax1, ay1, ax2, ay2, bx2, by2)) return true;
583
+ return false;
584
+ }
585
+ function polygonIntersectsRect(polygon, minX, minY, maxX, maxY) {
586
+ const n = polygon.length;
587
+ for (let i = 0; i < n; i++) {
588
+ const p = polygon[i];
589
+ if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {
590
+ return true;
591
+ }
592
+ }
593
+ if (pointInPolygon(minX, minY, polygon) || pointInPolygon(maxX, minY, polygon) || pointInPolygon(maxX, maxY, polygon) || pointInPolygon(minX, maxY, polygon)) {
594
+ return true;
595
+ }
596
+ const rx = [minX, maxX, maxX, minX];
597
+ const ry = [minY, minY, maxY, maxY];
598
+ for (let i = 0; i < n; i++) {
599
+ const a = polygon[i];
600
+ const b = polygon[(i + 1) % n];
601
+ for (let j = 0; j < 4; j++) {
602
+ const k = (j + 1) % 4;
603
+ if (segmentsIntersect(a.x, a.y, b.x, b.y, rx[j], ry[j], rx[k], ry[k])) {
604
+ return true;
605
+ }
606
+ }
607
+ }
608
+ return false;
609
+ }
610
+ function selectByBoxFromOidMap(oidNodeMap, box) {
611
+ const result = [];
612
+ const nodeBox = new Box3();
613
+ for (const [oid, node] of oidNodeMap) {
614
+ if (!node.bbox || node.bbox.length < 6) continue;
615
+ nodeBox.min.set(node.bbox[0], node.bbox[1], node.bbox[2]);
616
+ nodeBox.max.set(node.bbox[3], node.bbox[4], node.bbox[5]);
617
+ if (box.intersectsBox(nodeBox)) {
618
+ result.push(oid);
619
+ }
620
+ }
621
+ return result;
622
+ }
623
+ function selectByPolygonFromOidMap(oidNodeMap, polygon, axis = "xz") {
624
+ const result = [];
625
+ const polygon2D = polygon.map((p) => {
626
+ switch (axis) {
627
+ case "xy":
628
+ return new Vector2(p.x, p.y);
629
+ case "yz":
630
+ return new Vector2(p.y, p.z);
631
+ case "xz":
632
+ default:
633
+ return new Vector2(p.x, p.z);
634
+ }
635
+ });
636
+ for (const [oid, node] of oidNodeMap) {
637
+ if (!node.bbox || node.bbox.length < 6) continue;
638
+ let minU, minV, maxU, maxV;
639
+ switch (axis) {
640
+ case "xy":
641
+ minU = node.bbox[0];
642
+ minV = node.bbox[1];
643
+ maxU = node.bbox[3];
644
+ maxV = node.bbox[4];
645
+ break;
646
+ case "xz":
647
+ minU = node.bbox[0];
648
+ minV = node.bbox[2];
649
+ maxU = node.bbox[3];
650
+ maxV = node.bbox[5];
651
+ break;
652
+ case "yz":
653
+ minU = node.bbox[1];
654
+ minV = node.bbox[2];
655
+ maxU = node.bbox[4];
656
+ maxV = node.bbox[5];
657
+ break;
658
+ }
659
+ if (polygonIntersectsRect(polygon2D, minU, minV, maxU, maxV)) {
660
+ result.push(oid);
661
+ }
662
+ }
663
+ return result;
664
+ }
557
665
  const _camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
558
666
  class FullscreenTriangleGeometry extends BufferGeometry {
559
667
  constructor() {
@@ -1646,6 +1754,276 @@ class GLTFWorkerLoader extends Loader {
1646
1754
  });
1647
1755
  }
1648
1756
  }
1757
+ function ensureColor(color) {
1758
+ if (color instanceof Color) {
1759
+ return color;
1760
+ }
1761
+ return new Color(color);
1762
+ }
1763
+ function getMaterials(mesh) {
1764
+ const mat = mesh.material;
1765
+ if (!mat) return [];
1766
+ return Array.isArray(mat) ? mat : [mat];
1767
+ }
1768
+ function isColorMaterial(material) {
1769
+ return "color" in material && typeof material.color !== "undefined";
1770
+ }
1771
+ class ComponentColorModifier {
1772
+ constructor(context) {
1773
+ this.context = context;
1774
+ }
1775
+ _originalColors = /* @__PURE__ */ new Map();
1776
+ _originalOpacities = /* @__PURE__ */ new Map();
1777
+ /**
1778
+ * 根据 oid 数组修改构件颜色
1779
+ * @param oids 构件 OID 数组
1780
+ * @param color 颜色值,支持 hex 数字、颜色字符串(如 "#ff0000")、THREE.Color 对象
1781
+ */
1782
+ setColorByOids(oids, color) {
1783
+ const threeColor = ensureColor(color);
1784
+ for (const oid of oids) {
1785
+ const meshes = this.context.getMeshesByOid(oid);
1786
+ for (const mesh of meshes) {
1787
+ for (const material of getMaterials(mesh)) {
1788
+ if (isColorMaterial(material)) {
1789
+ if (!this._originalColors.has(material)) {
1790
+ this._originalColors.set(material, material.color.clone());
1791
+ }
1792
+ material.color.copy(threeColor);
1793
+ }
1794
+ }
1795
+ }
1796
+ }
1797
+ }
1798
+ /**
1799
+ * 恢复指定构件的颜色为修改前的状态
1800
+ * @param oids 构件 OID 数组
1801
+ */
1802
+ restoreColorByOids(oids) {
1803
+ for (const oid of oids) {
1804
+ const meshes = this.context.getMeshesByOid(oid);
1805
+ for (const mesh of meshes) {
1806
+ for (const material of getMaterials(mesh)) {
1807
+ if (isColorMaterial(material)) {
1808
+ const original = this._originalColors.get(material);
1809
+ if (original) {
1810
+ material.color.copy(original);
1811
+ this._originalColors.delete(material);
1812
+ }
1813
+ }
1814
+ }
1815
+ }
1816
+ }
1817
+ }
1818
+ /**
1819
+ * 根据 oid 数组修改构件颜色透明度
1820
+ * @param oids 构件 OID 数组
1821
+ * @param opacity 透明度,0-1,0 完全透明,1 完全不透明
1822
+ */
1823
+ setOpacityByOids(oids, opacity) {
1824
+ const clampedOpacity = Math.max(0, Math.min(1, opacity));
1825
+ for (const oid of oids) {
1826
+ const meshes = this.context.getMeshesByOid(oid);
1827
+ for (const mesh of meshes) {
1828
+ for (const material of getMaterials(mesh)) {
1829
+ if (!this._originalOpacities.has(material)) {
1830
+ this._originalOpacities.set(material, material.opacity);
1831
+ }
1832
+ material.opacity = clampedOpacity;
1833
+ material.transparent = clampedOpacity < 1;
1834
+ }
1835
+ }
1836
+ }
1837
+ }
1838
+ /**
1839
+ * 恢复指定构件的透明度为修改前的状态
1840
+ * @param oids 构件 OID 数组
1841
+ */
1842
+ restoreOpacityByOids(oids) {
1843
+ for (const oid of oids) {
1844
+ const meshes = this.context.getMeshesByOid(oid);
1845
+ for (const mesh of meshes) {
1846
+ for (const material of getMaterials(mesh)) {
1847
+ const original = this._originalOpacities.get(material);
1848
+ if (original !== void 0) {
1849
+ material.opacity = original;
1850
+ material.transparent = original < 1;
1851
+ this._originalOpacities.delete(material);
1852
+ }
1853
+ }
1854
+ }
1855
+ }
1856
+ }
1857
+ /**
1858
+ * 清除缓存的原始值,释放引用
1859
+ */
1860
+ dispose() {
1861
+ this._originalColors.clear();
1862
+ this._originalOpacities.clear();
1863
+ }
1864
+ }
1865
+ class InteractionFilter {
1866
+ constructor(context) {
1867
+ this.context = context;
1868
+ }
1869
+ frozenOids = /* @__PURE__ */ new Set();
1870
+ isolatedOids = /* @__PURE__ */ new Set();
1871
+ trackedMeshes = /* @__PURE__ */ new Map();
1872
+ meshListeners = /* @__PURE__ */ new Map();
1873
+ isPluginRemoving = false;
1874
+ isOidBlocked(oid) {
1875
+ if (this.frozenOids.has(oid)) return true;
1876
+ if (this.isolatedOids.size > 0 && !this.isolatedOids.has(oid)) return true;
1877
+ return false;
1878
+ }
1879
+ trackMesh(mesh, oid) {
1880
+ if (this.meshListeners.has(mesh)) return;
1881
+ const onAdded = () => {
1882
+ if (this.isPluginRemoving) return;
1883
+ mesh.userData._detachedParent = null;
1884
+ if (this.isOidBlocked(oid) && mesh.parent) {
1885
+ const parent = mesh.parent;
1886
+ this.isPluginRemoving = true;
1887
+ mesh.userData._detachedParent = parent;
1888
+ parent.remove(mesh);
1889
+ this.isPluginRemoving = false;
1890
+ }
1891
+ };
1892
+ const onRemoved = () => {
1893
+ if (this.isPluginRemoving) return;
1894
+ mesh.userData._detachedParent = null;
1895
+ };
1896
+ mesh.addEventListener("added", onAdded);
1897
+ mesh.addEventListener("removed", onRemoved);
1898
+ this.meshListeners.set(mesh, { onAdded, onRemoved });
1899
+ }
1900
+ untrackMesh(mesh) {
1901
+ const listeners = this.meshListeners.get(mesh);
1902
+ if (listeners) {
1903
+ mesh.removeEventListener("added", listeners.onAdded);
1904
+ mesh.removeEventListener("removed", listeners.onRemoved);
1905
+ this.meshListeners.delete(mesh);
1906
+ }
1907
+ mesh.userData._detachedParent = null;
1908
+ }
1909
+ onCollectorMeshChange(oid, newMeshes) {
1910
+ const tracked = this.trackedMeshes.get(oid);
1911
+ const newSet = new Set(newMeshes);
1912
+ if (tracked) {
1913
+ for (const mesh of tracked) {
1914
+ if (!newSet.has(mesh)) {
1915
+ this.untrackMesh(mesh);
1916
+ tracked.delete(mesh);
1917
+ }
1918
+ }
1919
+ }
1920
+ const trackSet = tracked || /* @__PURE__ */ new Set();
1921
+ for (const mesh of newMeshes) {
1922
+ if (!trackSet.has(mesh)) {
1923
+ this.trackMesh(mesh, oid);
1924
+ trackSet.add(mesh);
1925
+ }
1926
+ }
1927
+ this.trackedMeshes.set(oid, trackSet);
1928
+ }
1929
+ syncCollectorMeshes() {
1930
+ this.isPluginRemoving = true;
1931
+ for (const [oid, collector] of this.context.getCollectorCache()) {
1932
+ const blocked = this.isOidBlocked(oid);
1933
+ for (const mesh of collector.meshes) {
1934
+ if (!this.meshListeners.has(mesh)) continue;
1935
+ if (blocked) {
1936
+ if (mesh.parent && !mesh.userData._detachedParent) {
1937
+ const parent = mesh.parent;
1938
+ mesh.userData._detachedParent = parent;
1939
+ parent.remove(mesh);
1940
+ }
1941
+ } else {
1942
+ const storedParent = mesh.userData._detachedParent;
1943
+ if (storedParent && !mesh.parent) {
1944
+ storedParent.add(mesh);
1945
+ mesh.userData._detachedParent = null;
1946
+ }
1947
+ }
1948
+ }
1949
+ }
1950
+ this.isPluginRemoving = false;
1951
+ }
1952
+ onUnregisterCollector(oid) {
1953
+ const tracked = this.trackedMeshes.get(oid);
1954
+ if (tracked) {
1955
+ for (const mesh of tracked) {
1956
+ this.untrackMesh(mesh);
1957
+ }
1958
+ this.trackedMeshes.delete(oid);
1959
+ }
1960
+ }
1961
+ freezeByOids(oids) {
1962
+ for (const oid of oids) {
1963
+ this.frozenOids.add(oid);
1964
+ }
1965
+ this.syncCollectorMeshes();
1966
+ }
1967
+ freezeByOid(oid) {
1968
+ this.frozenOids.add(oid);
1969
+ this.syncCollectorMeshes();
1970
+ }
1971
+ unfreezeByOids(oids) {
1972
+ for (const oid of oids) {
1973
+ this.frozenOids.delete(oid);
1974
+ }
1975
+ this.syncCollectorMeshes();
1976
+ }
1977
+ unfreezeByOid(oid) {
1978
+ this.frozenOids.delete(oid);
1979
+ this.syncCollectorMeshes();
1980
+ }
1981
+ unfreeze() {
1982
+ this.frozenOids.clear();
1983
+ this.syncCollectorMeshes();
1984
+ }
1985
+ getFrozenOids() {
1986
+ return Array.from(this.frozenOids);
1987
+ }
1988
+ isolateByOids(oids) {
1989
+ for (const oid of oids) {
1990
+ this.isolatedOids.add(oid);
1991
+ }
1992
+ this.syncCollectorMeshes();
1993
+ }
1994
+ isolateByOid(oid) {
1995
+ this.isolatedOids.add(oid);
1996
+ this.syncCollectorMeshes();
1997
+ }
1998
+ unisolateByOids(oids) {
1999
+ for (const oid of oids) {
2000
+ this.isolatedOids.delete(oid);
2001
+ }
2002
+ this.syncCollectorMeshes();
2003
+ }
2004
+ unisolateByOid(oid) {
2005
+ this.isolatedOids.delete(oid);
2006
+ this.syncCollectorMeshes();
2007
+ }
2008
+ unisolate() {
2009
+ this.isolatedOids.clear();
2010
+ this.syncCollectorMeshes();
2011
+ }
2012
+ getIsolatedOids() {
2013
+ return Array.from(this.isolatedOids);
2014
+ }
2015
+ dispose() {
2016
+ for (const [, meshSet] of this.trackedMeshes) {
2017
+ for (const mesh of meshSet) {
2018
+ this.untrackMesh(mesh);
2019
+ }
2020
+ }
2021
+ this.trackedMeshes.clear();
2022
+ this.meshListeners.clear();
2023
+ this.frozenOids.clear();
2024
+ this.isolatedOids.clear();
2025
+ }
2026
+ }
1649
2027
  const DB_NAME = "GLTFParserPluginTilesCache";
1650
2028
  const DB_VERSION = 1;
1651
2029
  const STORE_NAME = "tiles";
@@ -1749,12 +2127,8 @@ class GLTFParserPlugin {
1749
2127
  // --- Model info properties ---
1750
2128
  _modelInfo = null;
1751
2129
  _modelInfoPromise = null;
1752
- // --- Interaction filter properties ---
1753
- _frozenOids = /* @__PURE__ */ new Set();
1754
- _isolatedOids = /* @__PURE__ */ new Set();
1755
- _trackedMeshes = /* @__PURE__ */ new Map();
1756
- _meshListeners = /* @__PURE__ */ new Map();
1757
- _isPluginRemoving = false;
2130
+ _interactionFilter;
2131
+ _componentColorModifier;
1758
2132
  // --- Mesh helper properties ---
1759
2133
  oids = [];
1760
2134
  renderer = null;
@@ -1777,6 +2151,12 @@ class GLTFParserPlugin {
1777
2151
  if (options?.renderer) {
1778
2152
  this.renderer = options.renderer;
1779
2153
  }
2154
+ this._interactionFilter = new InteractionFilter({
2155
+ getCollectorCache: () => this.collectorCache
2156
+ });
2157
+ this._componentColorModifier = new ComponentColorModifier({
2158
+ getMeshesByOid: (oid) => this._getMeshesByOidInternal(oid)
2159
+ });
1780
2160
  setMaxWorkers(this._options.maxWorkers);
1781
2161
  }
1782
2162
  /**
@@ -1935,71 +2315,6 @@ class GLTFParserPlugin {
1935
2315
  async getStructureData() {
1936
2316
  return this._ensureStructureLoaded();
1937
2317
  }
1938
- // =============================================
1939
- // Spatial Query Methods
1940
- // =============================================
1941
- _pointInPolygon(px, py, polygon) {
1942
- let inside = false;
1943
- const n = polygon.length;
1944
- for (let i = 0, j = n - 1; i < n; j = i++) {
1945
- const xi = polygon[i].x, yi = polygon[i].y;
1946
- const xj = polygon[j].x, yj = polygon[j].y;
1947
- if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi) {
1948
- inside = !inside;
1949
- }
1950
- }
1951
- return inside;
1952
- }
1953
- _segmentsIntersect(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) {
1954
- const cross = (ox, oy, ax, ay, bx, by) => (ax - ox) * (by - oy) - (ay - oy) * (bx - ox);
1955
- const d1 = cross(bx1, by1, bx2, by2, ax1, ay1);
1956
- const d2 = cross(bx1, by1, bx2, by2, ax2, ay2);
1957
- const d3 = cross(ax1, ay1, ax2, ay2, bx1, by1);
1958
- const d4 = cross(ax1, ay1, ax2, ay2, bx2, by2);
1959
- if ((d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0)) {
1960
- return true;
1961
- }
1962
- const onSeg = (px, py, qx, qy, rx, ry) => Math.min(px, qx) <= rx && rx <= Math.max(px, qx) && Math.min(py, qy) <= ry && ry <= Math.max(py, qy);
1963
- if (d1 === 0 && onSeg(bx1, by1, bx2, by2, ax1, ay1)) return true;
1964
- if (d2 === 0 && onSeg(bx1, by1, bx2, by2, ax2, ay2)) return true;
1965
- if (d3 === 0 && onSeg(ax1, ay1, ax2, ay2, bx1, by1)) return true;
1966
- if (d4 === 0 && onSeg(ax1, ay1, ax2, ay2, bx2, by2)) return true;
1967
- return false;
1968
- }
1969
- _polygonIntersectsRect(polygon, minX, minY, maxX, maxY) {
1970
- const n = polygon.length;
1971
- for (let i = 0; i < n; i++) {
1972
- const p = polygon[i];
1973
- if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {
1974
- return true;
1975
- }
1976
- }
1977
- if (this._pointInPolygon(minX, minY, polygon) || this._pointInPolygon(maxX, minY, polygon) || this._pointInPolygon(maxX, maxY, polygon) || this._pointInPolygon(minX, maxY, polygon)) {
1978
- return true;
1979
- }
1980
- const rx = [minX, maxX, maxX, minX];
1981
- const ry = [minY, minY, maxY, maxY];
1982
- for (let i = 0; i < n; i++) {
1983
- const a = polygon[i];
1984
- const b = polygon[(i + 1) % n];
1985
- for (let j = 0; j < 4; j++) {
1986
- const k = (j + 1) % 4;
1987
- if (this._segmentsIntersect(
1988
- a.x,
1989
- a.y,
1990
- b.x,
1991
- b.y,
1992
- rx[j],
1993
- ry[j],
1994
- rx[k],
1995
- ry[k]
1996
- )) {
1997
- return true;
1998
- }
1999
- }
2000
- }
2001
- return false;
2002
- }
2003
2318
  /**
2004
2319
  * 选择包围盒范围内的构件(包含相交和包含两种情况)
2005
2320
  * @param box 查询用的 Box3 范围,坐标系与 structure.json 中 bbox 一致
@@ -2007,17 +2322,7 @@ class GLTFParserPlugin {
2007
2322
  */
2008
2323
  async selectByBox(box) {
2009
2324
  await this._ensureStructureLoaded();
2010
- const result = [];
2011
- const nodeBox = new Box3();
2012
- for (const [oid, node] of this._oidNodeMap) {
2013
- if (!node.bbox || node.bbox.length < 6) continue;
2014
- nodeBox.min.set(node.bbox[0], node.bbox[1], node.bbox[2]);
2015
- nodeBox.max.set(node.bbox[3], node.bbox[4], node.bbox[5]);
2016
- if (box.intersectsBox(nodeBox)) {
2017
- result.push(oid);
2018
- }
2019
- }
2020
- return result;
2325
+ return selectByBoxFromOidMap(this._oidNodeMap, box);
2021
2326
  }
2022
2327
  /**
2023
2328
  * 选择多边形(平面投影)范围内的构件(包含相交和包含两种情况)
@@ -2030,46 +2335,7 @@ class GLTFParserPlugin {
2030
2335
  */
2031
2336
  async selectByPolygon(polygon, axis = "xz") {
2032
2337
  await this._ensureStructureLoaded();
2033
- const result = [];
2034
- const polygon2D = polygon.map((p) => {
2035
- switch (axis) {
2036
- case "xy":
2037
- return new Vector2(p.x, p.y);
2038
- case "yz":
2039
- return new Vector2(p.y, p.z);
2040
- case "xz":
2041
- default:
2042
- return new Vector2(p.x, p.z);
2043
- }
2044
- });
2045
- for (const [oid, node] of this._oidNodeMap) {
2046
- if (!node.bbox || node.bbox.length < 6) continue;
2047
- let minU, minV, maxU, maxV;
2048
- switch (axis) {
2049
- case "xy":
2050
- minU = node.bbox[0];
2051
- minV = node.bbox[1];
2052
- maxU = node.bbox[3];
2053
- maxV = node.bbox[4];
2054
- break;
2055
- case "xz":
2056
- minU = node.bbox[0];
2057
- minV = node.bbox[2];
2058
- maxU = node.bbox[3];
2059
- maxV = node.bbox[5];
2060
- break;
2061
- case "yz":
2062
- minU = node.bbox[1];
2063
- minV = node.bbox[2];
2064
- maxU = node.bbox[4];
2065
- maxV = node.bbox[5];
2066
- break;
2067
- }
2068
- if (this._polygonIntersectsRect(polygon2D, minU, minV, maxU, maxV)) {
2069
- result.push(oid);
2070
- }
2071
- }
2072
- return result;
2338
+ return selectByPolygonFromOidMap(this._oidNodeMap, polygon, axis);
2073
2339
  }
2074
2340
  // =============================================
2075
2341
  // Model Info Methods
@@ -2154,13 +2420,7 @@ class GLTFParserPlugin {
2154
2420
  const oid = collector.getOid();
2155
2421
  this.collectors.delete(collector);
2156
2422
  this.collectorCache.delete(oid);
2157
- const tracked = this._trackedMeshes.get(oid);
2158
- if (tracked) {
2159
- for (const mesh of tracked) {
2160
- this._untrackMesh(mesh);
2161
- }
2162
- this._trackedMeshes.delete(oid);
2163
- }
2423
+ this._interactionFilter.onUnregisterCollector(oid);
2164
2424
  }
2165
2425
  _updateWebGLLimits() {
2166
2426
  const gl = this.renderer.getContext();
@@ -2259,186 +2519,86 @@ class GLTFParserPlugin {
2259
2519
  queryFeatureFromIntersection(hit) {
2260
2520
  const result = queryFeatureFromIntersection(hit);
2261
2521
  if (result.isValid && result.oid !== void 0) {
2262
- if (this._frozenOids.has(result.oid)) {
2263
- return { isValid: false, error: "Component is frozen" };
2264
- }
2265
- if (this._isolatedOids.size > 0 && !this._isolatedOids.has(result.oid)) {
2266
- return { isValid: false, error: "Component is not in isolated set" };
2522
+ if (this._interactionFilter.isOidBlocked(result.oid)) {
2523
+ return {
2524
+ isValid: false,
2525
+ error: this._interactionFilter.getFrozenOids().includes(result.oid) ? "Component is frozen" : "Component is not in isolated set"
2526
+ };
2267
2527
  }
2268
2528
  }
2269
2529
  return result;
2270
2530
  }
2271
2531
  // =============================================
2272
- // Interaction Filter Methods
2532
+ // Interaction Filter Methods (delegated)
2273
2533
  // =============================================
2274
- _isOidBlocked(oid) {
2275
- if (this._frozenOids.has(oid)) return true;
2276
- if (this._isolatedOids.size > 0 && !this._isolatedOids.has(oid))
2277
- return true;
2278
- return false;
2279
- }
2280
- _trackMesh(mesh, oid) {
2281
- if (this._meshListeners.has(mesh)) return;
2282
- const onAdded = () => {
2283
- if (this._isPluginRemoving) return;
2284
- mesh.userData._detachedParent = null;
2285
- if (this._isOidBlocked(oid) && mesh.parent) {
2286
- const parent = mesh.parent;
2287
- this._isPluginRemoving = true;
2288
- mesh.userData._detachedParent = parent;
2289
- parent.remove(mesh);
2290
- this._isPluginRemoving = false;
2291
- }
2292
- };
2293
- const onRemoved = () => {
2294
- if (this._isPluginRemoving) return;
2295
- mesh.userData._detachedParent = null;
2296
- };
2297
- mesh.addEventListener("added", onAdded);
2298
- mesh.addEventListener("removed", onRemoved);
2299
- this._meshListeners.set(mesh, { onAdded, onRemoved });
2300
- }
2301
- _untrackMesh(mesh) {
2302
- const listeners = this._meshListeners.get(mesh);
2303
- if (listeners) {
2304
- mesh.removeEventListener("added", listeners.onAdded);
2305
- mesh.removeEventListener("removed", listeners.onRemoved);
2306
- this._meshListeners.delete(mesh);
2307
- }
2308
- mesh.userData._detachedParent = null;
2309
- }
2310
- _onCollectorMeshChange(oid, newMeshes) {
2311
- const tracked = this._trackedMeshes.get(oid);
2312
- const newSet = new Set(newMeshes);
2313
- if (tracked) {
2314
- for (const mesh of tracked) {
2315
- if (!newSet.has(mesh)) {
2316
- this._untrackMesh(mesh);
2317
- tracked.delete(mesh);
2318
- }
2319
- }
2320
- }
2321
- const trackSet = tracked || /* @__PURE__ */ new Set();
2322
- for (const mesh of newMeshes) {
2323
- if (!trackSet.has(mesh)) {
2324
- this._trackMesh(mesh, oid);
2325
- trackSet.add(mesh);
2326
- }
2327
- }
2328
- this._trackedMeshes.set(oid, trackSet);
2329
- }
2330
- _syncCollectorMeshes() {
2331
- this._isPluginRemoving = true;
2332
- for (const [oid, collector] of this.collectorCache) {
2333
- const blocked = this._isOidBlocked(oid);
2334
- for (const mesh of collector.meshes) {
2335
- if (!this._meshListeners.has(mesh)) continue;
2336
- if (blocked) {
2337
- if (mesh.parent && !mesh.userData._detachedParent) {
2338
- const parent = mesh.parent;
2339
- mesh.userData._detachedParent = parent;
2340
- this.unhideByOids([oid]);
2341
- }
2342
- } else {
2343
- const storedParent = mesh.userData._detachedParent;
2344
- if (storedParent && !mesh.parent) {
2345
- storedParent.add(mesh);
2346
- mesh.userData._detachedParent = null;
2347
- }
2348
- }
2349
- }
2350
- }
2351
- this._isPluginRemoving = false;
2352
- }
2353
- /**
2354
- * 冻结指定构件,被冻结的构件不再响应任何交互和事件
2355
- */
2356
2534
  freezeByOids(oids) {
2357
- for (const oid of oids) {
2358
- this._frozenOids.add(oid);
2359
- }
2360
- this._syncCollectorMeshes();
2535
+ this._interactionFilter.freezeByOids(oids);
2361
2536
  }
2362
- /**
2363
- * 冻结单个构件
2364
- */
2365
2537
  freezeByOid(oid) {
2366
- this._frozenOids.add(oid);
2367
- this._syncCollectorMeshes();
2538
+ this._interactionFilter.freezeByOid(oid);
2368
2539
  }
2369
- /**
2370
- * 解冻指定构件
2371
- */
2372
2540
  unfreezeByOids(oids) {
2373
- for (const oid of oids) {
2374
- this._frozenOids.delete(oid);
2375
- }
2376
- this._syncCollectorMeshes();
2541
+ this._interactionFilter.unfreezeByOids(oids);
2377
2542
  }
2378
- /**
2379
- * 解冻单个构件
2380
- */
2381
2543
  unfreezeByOid(oid) {
2382
- this._frozenOids.delete(oid);
2383
- this._syncCollectorMeshes();
2544
+ this._interactionFilter.unfreezeByOid(oid);
2384
2545
  }
2385
- /**
2386
- * 解冻全部构件
2387
- */
2388
2546
  unfreeze() {
2389
- this._frozenOids.clear();
2390
- this._syncCollectorMeshes();
2547
+ this._interactionFilter.unfreeze();
2391
2548
  }
2392
- /**
2393
- * 获取当前被冻结的 OID 数组
2394
- */
2395
2549
  getFrozenOids() {
2396
- return Array.from(this._frozenOids);
2550
+ return this._interactionFilter.getFrozenOids();
2397
2551
  }
2398
- /**
2399
- * 隔离指定构件,隔离模式下只有被隔离的构件才能响应交互和事件
2400
- */
2401
2552
  isolateByOids(oids) {
2402
- for (const oid of oids) {
2403
- this._isolatedOids.add(oid);
2404
- }
2405
- this._syncCollectorMeshes();
2553
+ this._interactionFilter.isolateByOids(oids);
2406
2554
  }
2407
- /**
2408
- * 往隔离集合中添加单个构件
2409
- */
2410
2555
  isolateByOid(oid) {
2411
- this._isolatedOids.add(oid);
2412
- this._syncCollectorMeshes();
2556
+ this._interactionFilter.isolateByOid(oid);
2557
+ }
2558
+ unisolateByOids(oids) {
2559
+ this._interactionFilter.unisolateByOids(oids);
2413
2560
  }
2561
+ unisolateByOid(oid) {
2562
+ this._interactionFilter.unisolateByOid(oid);
2563
+ }
2564
+ unisolate() {
2565
+ this._interactionFilter.unisolate();
2566
+ }
2567
+ getIsolatedOids() {
2568
+ return this._interactionFilter.getIsolatedOids();
2569
+ }
2570
+ // =============================================
2571
+ // Component Color Modifier Methods (delegated)
2572
+ // =============================================
2414
2573
  /**
2415
- * 取消隔离指定构件
2574
+ * 根据 oid 数组修改构件颜色
2575
+ * @param oids 构件 OID 数组
2576
+ * @param color 颜色值,支持 hex 数字(如 0xff0000)、颜色字符串(如 "#ff0000")、THREE.Color 对象
2416
2577
  */
2417
- unisolateByOids(oids) {
2418
- for (const oid of oids) {
2419
- this._isolatedOids.delete(oid);
2420
- }
2421
- this._syncCollectorMeshes();
2578
+ setColorByOids(oids, color) {
2579
+ this._componentColorModifier.setColorByOids(oids, color);
2422
2580
  }
2423
2581
  /**
2424
- * 从隔离集合中移除单个构件
2582
+ * 根据 oid 数组修改构件颜色透明度
2583
+ * @param oids 构件 OID 数组
2584
+ * @param opacity 透明度,0-1,0 完全透明,1 完全不透明
2425
2585
  */
2426
- unisolateByOid(oid) {
2427
- this._isolatedOids.delete(oid);
2428
- this._syncCollectorMeshes();
2586
+ setOpacityByOids(oids, opacity) {
2587
+ this._componentColorModifier.setOpacityByOids(oids, opacity);
2429
2588
  }
2430
2589
  /**
2431
- * 取消全部隔离,恢复所有构件的交互能力
2590
+ * 恢复指定构件的颜色为修改前的状态
2591
+ * @param oids 构件 OID 数组
2432
2592
  */
2433
- unisolate() {
2434
- this._isolatedOids.clear();
2435
- this._syncCollectorMeshes();
2593
+ restoreColorByOids(oids) {
2594
+ this._componentColorModifier.restoreColorByOids(oids);
2436
2595
  }
2437
2596
  /**
2438
- * 获取当前被隔离的 OID 数组
2597
+ * 恢复指定构件的透明度为修改前的状态
2598
+ * @param oids 构件 OID 数组
2439
2599
  */
2440
- getIsolatedOids() {
2441
- return Array.from(this._isolatedOids);
2600
+ restoreOpacityByOids(oids) {
2601
+ this._componentColorModifier.restoreOpacityByOids(oids);
2442
2602
  }
2443
2603
  /**
2444
2604
  * 内部方法:根据 oid 获取 mesh 数组
@@ -2469,9 +2629,9 @@ class GLTFParserPlugin {
2469
2629
  }
2470
2630
  const collector = new MeshCollector(oid, this);
2471
2631
  this.collectorCache.set(oid, collector);
2472
- this._onCollectorMeshChange(oid, collector.meshes);
2632
+ this._interactionFilter.onCollectorMeshChange(oid, collector.meshes);
2473
2633
  collector.addEventListener("mesh-change", (event) => {
2474
- this._onCollectorMeshChange(oid, event.meshes);
2634
+ this._interactionFilter.onCollectorMeshChange(oid, event.meshes);
2475
2635
  });
2476
2636
  return collector;
2477
2637
  }
@@ -2527,15 +2687,8 @@ class GLTFParserPlugin {
2527
2687
  this._structurePromise = null;
2528
2688
  this._modelInfo = null;
2529
2689
  this._modelInfoPromise = null;
2530
- for (const [, meshSet] of this._trackedMeshes) {
2531
- for (const mesh of meshSet) {
2532
- this._untrackMesh(mesh);
2533
- }
2534
- }
2535
- this._trackedMeshes.clear();
2536
- this._meshListeners.clear();
2537
- this._frozenOids.clear();
2538
- this._isolatedOids.clear();
2690
+ this._interactionFilter.dispose();
2691
+ this._componentColorModifier.dispose();
2539
2692
  this._loader = null;
2540
2693
  this.tiles = null;
2541
2694
  }