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.
- package/build/gltf-parser-plugin.module.js +441 -288
- package/build/gltf-parser-plugin.module.js.map +1 -1
- package/build/index.d.ts +23 -45
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Mesh, Vector3, Triangle, BufferGeometry, EventDispatcher, DataTexture, RGBAFormat, UnsignedByteType, SRGBColorSpace, Texture, MeshStandardMaterial, DoubleSide, FrontSide, BufferAttribute, OrthographicCamera, Float32BufferAttribute,
|
|
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
|
-
|
|
1753
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
2263
|
-
return {
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
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
|
-
|
|
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.
|
|
2367
|
-
this._syncCollectorMeshes();
|
|
2538
|
+
this._interactionFilter.freezeByOid(oid);
|
|
2368
2539
|
}
|
|
2369
|
-
/**
|
|
2370
|
-
* 解冻指定构件
|
|
2371
|
-
*/
|
|
2372
2540
|
unfreezeByOids(oids) {
|
|
2373
|
-
|
|
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.
|
|
2383
|
-
this._syncCollectorMeshes();
|
|
2544
|
+
this._interactionFilter.unfreezeByOid(oid);
|
|
2384
2545
|
}
|
|
2385
|
-
/**
|
|
2386
|
-
* 解冻全部构件
|
|
2387
|
-
*/
|
|
2388
2546
|
unfreeze() {
|
|
2389
|
-
this.
|
|
2390
|
-
this._syncCollectorMeshes();
|
|
2547
|
+
this._interactionFilter.unfreeze();
|
|
2391
2548
|
}
|
|
2392
|
-
/**
|
|
2393
|
-
* 获取当前被冻结的 OID 数组
|
|
2394
|
-
*/
|
|
2395
2549
|
getFrozenOids() {
|
|
2396
|
-
return
|
|
2550
|
+
return this._interactionFilter.getFrozenOids();
|
|
2397
2551
|
}
|
|
2398
|
-
/**
|
|
2399
|
-
* 隔离指定构件,隔离模式下只有被隔离的构件才能响应交互和事件
|
|
2400
|
-
*/
|
|
2401
2552
|
isolateByOids(oids) {
|
|
2402
|
-
|
|
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.
|
|
2412
|
-
|
|
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
|
-
|
|
2418
|
-
|
|
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
|
-
|
|
2427
|
-
this.
|
|
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
|
-
|
|
2434
|
-
this.
|
|
2435
|
-
this._syncCollectorMeshes();
|
|
2593
|
+
restoreColorByOids(oids) {
|
|
2594
|
+
this._componentColorModifier.restoreColorByOids(oids);
|
|
2436
2595
|
}
|
|
2437
2596
|
/**
|
|
2438
|
-
*
|
|
2597
|
+
* 恢复指定构件的透明度为修改前的状态
|
|
2598
|
+
* @param oids 构件 OID 数组
|
|
2439
2599
|
*/
|
|
2440
|
-
|
|
2441
|
-
|
|
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.
|
|
2632
|
+
this._interactionFilter.onCollectorMeshChange(oid, collector.meshes);
|
|
2473
2633
|
collector.addEventListener("mesh-change", (event) => {
|
|
2474
|
-
this.
|
|
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
|
-
|
|
2531
|
-
|
|
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
|
}
|