gltf-parser-plugin 1.1.2 → 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 +442 -278
- package/build/gltf-parser-plugin.module.js.map +1 -1
- package/build/index.d.ts +26 -48
- 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,21 +2322,11 @@ 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
|
* 选择多边形(平面投影)范围内的构件(包含相交和包含两种情况)
|
|
2024
|
-
* @param polygon 多边形顶点数组(
|
|
2329
|
+
* @param polygon 多边形顶点数组(Vector3),按顺序连接构成闭合多边形
|
|
2025
2330
|
* @param axis 投影平面,决定使用 bbox 的哪两个轴做 2D 判定
|
|
2026
2331
|
* - 'xz'(默认):俯视图,取 bbox 的 x/z 坐标
|
|
2027
2332
|
* - 'xy':正视图,取 bbox 的 x/y 坐标
|
|
@@ -2030,35 +2335,7 @@ class GLTFParserPlugin {
|
|
|
2030
2335
|
*/
|
|
2031
2336
|
async selectByPolygon(polygon, axis = "xz") {
|
|
2032
2337
|
await this._ensureStructureLoaded();
|
|
2033
|
-
|
|
2034
|
-
for (const [oid, node] of this._oidNodeMap) {
|
|
2035
|
-
if (!node.bbox || node.bbox.length < 6) continue;
|
|
2036
|
-
let minU, minV, maxU, maxV;
|
|
2037
|
-
switch (axis) {
|
|
2038
|
-
case "xy":
|
|
2039
|
-
minU = node.bbox[0];
|
|
2040
|
-
minV = node.bbox[1];
|
|
2041
|
-
maxU = node.bbox[3];
|
|
2042
|
-
maxV = node.bbox[4];
|
|
2043
|
-
break;
|
|
2044
|
-
case "xz":
|
|
2045
|
-
minU = node.bbox[0];
|
|
2046
|
-
minV = node.bbox[2];
|
|
2047
|
-
maxU = node.bbox[3];
|
|
2048
|
-
maxV = node.bbox[5];
|
|
2049
|
-
break;
|
|
2050
|
-
case "yz":
|
|
2051
|
-
minU = node.bbox[1];
|
|
2052
|
-
minV = node.bbox[2];
|
|
2053
|
-
maxU = node.bbox[4];
|
|
2054
|
-
maxV = node.bbox[5];
|
|
2055
|
-
break;
|
|
2056
|
-
}
|
|
2057
|
-
if (this._polygonIntersectsRect(polygon, minU, minV, maxU, maxV)) {
|
|
2058
|
-
result.push(oid);
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
return result;
|
|
2338
|
+
return selectByPolygonFromOidMap(this._oidNodeMap, polygon, axis);
|
|
2062
2339
|
}
|
|
2063
2340
|
// =============================================
|
|
2064
2341
|
// Model Info Methods
|
|
@@ -2143,13 +2420,7 @@ class GLTFParserPlugin {
|
|
|
2143
2420
|
const oid = collector.getOid();
|
|
2144
2421
|
this.collectors.delete(collector);
|
|
2145
2422
|
this.collectorCache.delete(oid);
|
|
2146
|
-
|
|
2147
|
-
if (tracked) {
|
|
2148
|
-
for (const mesh of tracked) {
|
|
2149
|
-
this._untrackMesh(mesh);
|
|
2150
|
-
}
|
|
2151
|
-
this._trackedMeshes.delete(oid);
|
|
2152
|
-
}
|
|
2423
|
+
this._interactionFilter.onUnregisterCollector(oid);
|
|
2153
2424
|
}
|
|
2154
2425
|
_updateWebGLLimits() {
|
|
2155
2426
|
const gl = this.renderer.getContext();
|
|
@@ -2248,186 +2519,86 @@ class GLTFParserPlugin {
|
|
|
2248
2519
|
queryFeatureFromIntersection(hit) {
|
|
2249
2520
|
const result = queryFeatureFromIntersection(hit);
|
|
2250
2521
|
if (result.isValid && result.oid !== void 0) {
|
|
2251
|
-
if (this.
|
|
2252
|
-
return {
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
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
|
+
};
|
|
2256
2527
|
}
|
|
2257
2528
|
}
|
|
2258
2529
|
return result;
|
|
2259
2530
|
}
|
|
2260
2531
|
// =============================================
|
|
2261
|
-
// Interaction Filter Methods
|
|
2532
|
+
// Interaction Filter Methods (delegated)
|
|
2262
2533
|
// =============================================
|
|
2263
|
-
_isOidBlocked(oid) {
|
|
2264
|
-
if (this._frozenOids.has(oid)) return true;
|
|
2265
|
-
if (this._isolatedOids.size > 0 && !this._isolatedOids.has(oid))
|
|
2266
|
-
return true;
|
|
2267
|
-
return false;
|
|
2268
|
-
}
|
|
2269
|
-
_trackMesh(mesh, oid) {
|
|
2270
|
-
if (this._meshListeners.has(mesh)) return;
|
|
2271
|
-
const onAdded = () => {
|
|
2272
|
-
if (this._isPluginRemoving) return;
|
|
2273
|
-
mesh.userData._detachedParent = null;
|
|
2274
|
-
if (this._isOidBlocked(oid) && mesh.parent) {
|
|
2275
|
-
const parent = mesh.parent;
|
|
2276
|
-
this._isPluginRemoving = true;
|
|
2277
|
-
mesh.userData._detachedParent = parent;
|
|
2278
|
-
parent.remove(mesh);
|
|
2279
|
-
this._isPluginRemoving = false;
|
|
2280
|
-
}
|
|
2281
|
-
};
|
|
2282
|
-
const onRemoved = () => {
|
|
2283
|
-
if (this._isPluginRemoving) return;
|
|
2284
|
-
mesh.userData._detachedParent = null;
|
|
2285
|
-
};
|
|
2286
|
-
mesh.addEventListener("added", onAdded);
|
|
2287
|
-
mesh.addEventListener("removed", onRemoved);
|
|
2288
|
-
this._meshListeners.set(mesh, { onAdded, onRemoved });
|
|
2289
|
-
}
|
|
2290
|
-
_untrackMesh(mesh) {
|
|
2291
|
-
const listeners = this._meshListeners.get(mesh);
|
|
2292
|
-
if (listeners) {
|
|
2293
|
-
mesh.removeEventListener("added", listeners.onAdded);
|
|
2294
|
-
mesh.removeEventListener("removed", listeners.onRemoved);
|
|
2295
|
-
this._meshListeners.delete(mesh);
|
|
2296
|
-
}
|
|
2297
|
-
mesh.userData._detachedParent = null;
|
|
2298
|
-
}
|
|
2299
|
-
_onCollectorMeshChange(oid, newMeshes) {
|
|
2300
|
-
const tracked = this._trackedMeshes.get(oid);
|
|
2301
|
-
const newSet = new Set(newMeshes);
|
|
2302
|
-
if (tracked) {
|
|
2303
|
-
for (const mesh of tracked) {
|
|
2304
|
-
if (!newSet.has(mesh)) {
|
|
2305
|
-
this._untrackMesh(mesh);
|
|
2306
|
-
tracked.delete(mesh);
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
const trackSet = tracked || /* @__PURE__ */ new Set();
|
|
2311
|
-
for (const mesh of newMeshes) {
|
|
2312
|
-
if (!trackSet.has(mesh)) {
|
|
2313
|
-
this._trackMesh(mesh, oid);
|
|
2314
|
-
trackSet.add(mesh);
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
this._trackedMeshes.set(oid, trackSet);
|
|
2318
|
-
}
|
|
2319
|
-
_syncCollectorMeshes() {
|
|
2320
|
-
this._isPluginRemoving = true;
|
|
2321
|
-
for (const [oid, collector] of this.collectorCache) {
|
|
2322
|
-
const blocked = this._isOidBlocked(oid);
|
|
2323
|
-
for (const mesh of collector.meshes) {
|
|
2324
|
-
if (!this._meshListeners.has(mesh)) continue;
|
|
2325
|
-
if (blocked) {
|
|
2326
|
-
if (mesh.parent && !mesh.userData._detachedParent) {
|
|
2327
|
-
const parent = mesh.parent;
|
|
2328
|
-
mesh.userData._detachedParent = parent;
|
|
2329
|
-
this.unhideByOids([oid]);
|
|
2330
|
-
}
|
|
2331
|
-
} else {
|
|
2332
|
-
const storedParent = mesh.userData._detachedParent;
|
|
2333
|
-
if (storedParent && !mesh.parent) {
|
|
2334
|
-
storedParent.add(mesh);
|
|
2335
|
-
mesh.userData._detachedParent = null;
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
this._isPluginRemoving = false;
|
|
2341
|
-
}
|
|
2342
|
-
/**
|
|
2343
|
-
* 冻结指定构件,被冻结的构件不再响应任何交互和事件
|
|
2344
|
-
*/
|
|
2345
2534
|
freezeByOids(oids) {
|
|
2346
|
-
|
|
2347
|
-
this._frozenOids.add(oid);
|
|
2348
|
-
}
|
|
2349
|
-
this._syncCollectorMeshes();
|
|
2535
|
+
this._interactionFilter.freezeByOids(oids);
|
|
2350
2536
|
}
|
|
2351
|
-
/**
|
|
2352
|
-
* 冻结单个构件
|
|
2353
|
-
*/
|
|
2354
2537
|
freezeByOid(oid) {
|
|
2355
|
-
this.
|
|
2356
|
-
this._syncCollectorMeshes();
|
|
2538
|
+
this._interactionFilter.freezeByOid(oid);
|
|
2357
2539
|
}
|
|
2358
|
-
/**
|
|
2359
|
-
* 解冻指定构件
|
|
2360
|
-
*/
|
|
2361
2540
|
unfreezeByOids(oids) {
|
|
2362
|
-
|
|
2363
|
-
this._frozenOids.delete(oid);
|
|
2364
|
-
}
|
|
2365
|
-
this._syncCollectorMeshes();
|
|
2541
|
+
this._interactionFilter.unfreezeByOids(oids);
|
|
2366
2542
|
}
|
|
2367
|
-
/**
|
|
2368
|
-
* 解冻单个构件
|
|
2369
|
-
*/
|
|
2370
2543
|
unfreezeByOid(oid) {
|
|
2371
|
-
this.
|
|
2372
|
-
this._syncCollectorMeshes();
|
|
2544
|
+
this._interactionFilter.unfreezeByOid(oid);
|
|
2373
2545
|
}
|
|
2374
|
-
/**
|
|
2375
|
-
* 解冻全部构件
|
|
2376
|
-
*/
|
|
2377
2546
|
unfreeze() {
|
|
2378
|
-
this.
|
|
2379
|
-
this._syncCollectorMeshes();
|
|
2547
|
+
this._interactionFilter.unfreeze();
|
|
2380
2548
|
}
|
|
2381
|
-
/**
|
|
2382
|
-
* 获取当前被冻结的 OID 数组
|
|
2383
|
-
*/
|
|
2384
2549
|
getFrozenOids() {
|
|
2385
|
-
return
|
|
2550
|
+
return this._interactionFilter.getFrozenOids();
|
|
2386
2551
|
}
|
|
2387
|
-
/**
|
|
2388
|
-
* 隔离指定构件,隔离模式下只有被隔离的构件才能响应交互和事件
|
|
2389
|
-
*/
|
|
2390
2552
|
isolateByOids(oids) {
|
|
2391
|
-
|
|
2392
|
-
this._isolatedOids.add(oid);
|
|
2393
|
-
}
|
|
2394
|
-
this._syncCollectorMeshes();
|
|
2553
|
+
this._interactionFilter.isolateByOids(oids);
|
|
2395
2554
|
}
|
|
2396
|
-
/**
|
|
2397
|
-
* 往隔离集合中添加单个构件
|
|
2398
|
-
*/
|
|
2399
2555
|
isolateByOid(oid) {
|
|
2400
|
-
this.
|
|
2401
|
-
|
|
2556
|
+
this._interactionFilter.isolateByOid(oid);
|
|
2557
|
+
}
|
|
2558
|
+
unisolateByOids(oids) {
|
|
2559
|
+
this._interactionFilter.unisolateByOids(oids);
|
|
2402
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
|
+
// =============================================
|
|
2403
2573
|
/**
|
|
2404
|
-
*
|
|
2574
|
+
* 根据 oid 数组修改构件颜色
|
|
2575
|
+
* @param oids 构件 OID 数组
|
|
2576
|
+
* @param color 颜色值,支持 hex 数字(如 0xff0000)、颜色字符串(如 "#ff0000")、THREE.Color 对象
|
|
2405
2577
|
*/
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
this._isolatedOids.delete(oid);
|
|
2409
|
-
}
|
|
2410
|
-
this._syncCollectorMeshes();
|
|
2578
|
+
setColorByOids(oids, color) {
|
|
2579
|
+
this._componentColorModifier.setColorByOids(oids, color);
|
|
2411
2580
|
}
|
|
2412
2581
|
/**
|
|
2413
|
-
*
|
|
2582
|
+
* 根据 oid 数组修改构件颜色透明度
|
|
2583
|
+
* @param oids 构件 OID 数组
|
|
2584
|
+
* @param opacity 透明度,0-1,0 完全透明,1 完全不透明
|
|
2414
2585
|
*/
|
|
2415
|
-
|
|
2416
|
-
this.
|
|
2417
|
-
this._syncCollectorMeshes();
|
|
2586
|
+
setOpacityByOids(oids, opacity) {
|
|
2587
|
+
this._componentColorModifier.setOpacityByOids(oids, opacity);
|
|
2418
2588
|
}
|
|
2419
2589
|
/**
|
|
2420
|
-
*
|
|
2590
|
+
* 恢复指定构件的颜色为修改前的状态
|
|
2591
|
+
* @param oids 构件 OID 数组
|
|
2421
2592
|
*/
|
|
2422
|
-
|
|
2423
|
-
this.
|
|
2424
|
-
this._syncCollectorMeshes();
|
|
2593
|
+
restoreColorByOids(oids) {
|
|
2594
|
+
this._componentColorModifier.restoreColorByOids(oids);
|
|
2425
2595
|
}
|
|
2426
2596
|
/**
|
|
2427
|
-
*
|
|
2597
|
+
* 恢复指定构件的透明度为修改前的状态
|
|
2598
|
+
* @param oids 构件 OID 数组
|
|
2428
2599
|
*/
|
|
2429
|
-
|
|
2430
|
-
|
|
2600
|
+
restoreOpacityByOids(oids) {
|
|
2601
|
+
this._componentColorModifier.restoreOpacityByOids(oids);
|
|
2431
2602
|
}
|
|
2432
2603
|
/**
|
|
2433
2604
|
* 内部方法:根据 oid 获取 mesh 数组
|
|
@@ -2458,9 +2629,9 @@ class GLTFParserPlugin {
|
|
|
2458
2629
|
}
|
|
2459
2630
|
const collector = new MeshCollector(oid, this);
|
|
2460
2631
|
this.collectorCache.set(oid, collector);
|
|
2461
|
-
this.
|
|
2632
|
+
this._interactionFilter.onCollectorMeshChange(oid, collector.meshes);
|
|
2462
2633
|
collector.addEventListener("mesh-change", (event) => {
|
|
2463
|
-
this.
|
|
2634
|
+
this._interactionFilter.onCollectorMeshChange(oid, event.meshes);
|
|
2464
2635
|
});
|
|
2465
2636
|
return collector;
|
|
2466
2637
|
}
|
|
@@ -2516,15 +2687,8 @@ class GLTFParserPlugin {
|
|
|
2516
2687
|
this._structurePromise = null;
|
|
2517
2688
|
this._modelInfo = null;
|
|
2518
2689
|
this._modelInfoPromise = null;
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
this._untrackMesh(mesh);
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
|
-
this._trackedMeshes.clear();
|
|
2525
|
-
this._meshListeners.clear();
|
|
2526
|
-
this._frozenOids.clear();
|
|
2527
|
-
this._isolatedOids.clear();
|
|
2690
|
+
this._interactionFilter.dispose();
|
|
2691
|
+
this._componentColorModifier.dispose();
|
|
2528
2692
|
this._loader = null;
|
|
2529
2693
|
this.tiles = null;
|
|
2530
2694
|
}
|