fl-web-component 2.0.19-beta.1 → 2.0.19-beta.3
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/dist/fl-web-component.common.js +307 -220
- package/dist/fl-web-component.common.js.map +1 -1
- package/dist/fl-web-component.css +1 -1
- package/package.json +1 -1
- package/packages/components/com-graphics/index.vue +25 -5
- package/packages/utils/StreamLoaderParser.worker.js +20 -20
- package/src/utils/instance-parser.js +64 -64
- package/src/utils/threejs/measure-clear-distance.js +149 -123
|
@@ -45,10 +45,17 @@ MeasureClearDistance.prototype = {
|
|
|
45
45
|
|
|
46
46
|
_this.raycaster.setFromCamera(mouse, this.camera);
|
|
47
47
|
let intersects = _this.raycaster.intersectObjects(_this.scene.children, true);
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
let l = intersects.length
|
|
49
|
+
let obj = null
|
|
50
|
+
if (l > 0) {
|
|
51
|
+
for (let index = 0; index < l; index++) {
|
|
52
|
+
if (intersects[index].object.type === 'Mesh') {
|
|
53
|
+
obj = intersects[index]
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
}
|
|
50
57
|
}
|
|
51
|
-
return
|
|
58
|
+
return obj
|
|
52
59
|
},
|
|
53
60
|
createLine(p1, p2, config = { color: 0xff0000 }) {
|
|
54
61
|
const lineMaterial = new THREE.LineBasicMaterial({
|
|
@@ -91,10 +98,15 @@ MeasureClearDistance.prototype = {
|
|
|
91
98
|
let lastTime = new Date().getTime();
|
|
92
99
|
if (lastTime - this.firstTime < 300) {
|
|
93
100
|
const measureObj = _this.getPosition(e);
|
|
94
|
-
|
|
101
|
+
console.log(measureObj)
|
|
95
102
|
if (measureObj) {
|
|
96
103
|
_this.selectedObjects.push(measureObj)
|
|
97
|
-
|
|
104
|
+
// 高亮选中的
|
|
105
|
+
// const instanceInfo = measureObj.object.userData?.instancesMap?.get(measureObj.object.name);
|
|
106
|
+
// const { instanceIndex } = instanceInfo;
|
|
107
|
+
let instanceIndex = measureObj.instanceId
|
|
108
|
+
measureObj.object.setColorAt(instanceIndex, new THREE.Color(0x53a8ff));
|
|
109
|
+
measureObj.object.instanceColor.needsUpdate = true;
|
|
98
110
|
if (_this.selectedObjects.length % 2 === 0) {
|
|
99
111
|
_this.calculateClearDistance()
|
|
100
112
|
}
|
|
@@ -109,13 +121,29 @@ MeasureClearDistance.prototype = {
|
|
|
109
121
|
const obj1 = this.selectedObjects[l - 2];
|
|
110
122
|
const obj2 = this.selectedObjects[l - 1];
|
|
111
123
|
const isParallel = this.checkParallelism(obj1.object, obj1.instanceId, obj2.object, obj2.instanceId);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (pts1.length === 0 || pts2.length === 0) {
|
|
124
|
+
|
|
125
|
+
if (!isParallel) {
|
|
126
|
+
Message.warning('请选择两个平行的物体');
|
|
116
127
|
return
|
|
117
128
|
}
|
|
118
|
-
|
|
129
|
+
// 全局采样
|
|
130
|
+
const localPts1 = this.sampleGlobal(obj1.object.geometry, 1000);
|
|
131
|
+
const localPts2 = this.sampleGlobal(obj2.object.geometry, 1000);
|
|
132
|
+
const wm1 = this.getInstanceWorldMatrix(obj1.object, obj1.instanceId);
|
|
133
|
+
const wm2 = this.getInstanceWorldMatrix(obj2.object, obj2.instanceId);
|
|
134
|
+
const worldPts1 = localPts1.map(p => p.clone().applyMatrix4(wm1));
|
|
135
|
+
const worldPts2 = localPts2.map(p => p.clone().applyMatrix4(wm2));
|
|
136
|
+
let closestPair = this.findClosestPair(worldPts1, worldPts2);
|
|
137
|
+
// 局部精细搜索
|
|
138
|
+
const radius = closestPair.distance * 2 + 0.1;
|
|
139
|
+
const localPts1Fine = this.sampleLocal(obj1.object, obj1.instanceId, closestPair.point1, radius, 1000);
|
|
140
|
+
const localPts2Fine = this.sampleLocal(obj2.object, obj2.instanceId, closestPair.point2, radius, 1000);
|
|
141
|
+
const combined1 = worldPts1.filter(p => p.distanceTo(closestPair.point1) < radius).concat(localPts1Fine);
|
|
142
|
+
const combined2 = worldPts2.filter(p => p.distanceTo(closestPair.point2) < radius).concat(localPts2Fine);
|
|
143
|
+
if (combined1.length > 0 && combined2.length > 0) {
|
|
144
|
+
closestPair = this.findClosestPair(combined1, combined2, true);
|
|
145
|
+
}
|
|
146
|
+
|
|
119
147
|
const position = new THREE.Vector3((closestPair.point1.x + closestPair.point2.x) / 2, (closestPair.point1.y + closestPair.point2.y) / 2, (closestPair.point1.z + closestPair.point2.z) / 2);
|
|
120
148
|
// 将测量结果显示在页面中
|
|
121
149
|
const circleTag1 = this.createLabel('circle-tag', '', closestPair.point1);
|
|
@@ -131,109 +159,84 @@ MeasureClearDistance.prototype = {
|
|
|
131
159
|
this.scene.add(line);
|
|
132
160
|
this.scene.add(label);
|
|
133
161
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
let localPoints = this.sampleGeometrySurface(geometry, maxPoints);
|
|
138
|
-
const worldMatrix = this.getInstanceWorldMatrix(instancedMesh, instanceId);
|
|
139
|
-
const worldPoints = localPoints.map(p => p.clone().applyMatrix4(worldMatrix));
|
|
140
|
-
return worldPoints.slice(0, maxPoints);
|
|
141
|
-
},
|
|
142
|
-
sampleGeometrySurface(geometry, maxPoints = 600) {
|
|
143
|
-
const positions = geometry.getAttribute('position');
|
|
144
|
-
if (!positions) return [];
|
|
145
|
-
|
|
146
|
-
// 获取三角形(优先使用索引)
|
|
147
|
-
let triangles = [];
|
|
148
|
-
if (geometry.index) {
|
|
149
|
-
const index = geometry.index.array;
|
|
150
|
-
for (let i = 0; i < index.length; i += 3) {
|
|
151
|
-
const a = new THREE.Vector3(positions.getX(index[i]), positions.getY(index[i]), positions.getZ(index[i]));
|
|
152
|
-
const b = new THREE.Vector3(positions.getX(index[i + 1]), positions.getY(index[i + 1]), positions.getZ(index[i + 1]));
|
|
153
|
-
const c = new THREE.Vector3(positions.getX(index[i + 2]), positions.getY(index[i + 2]), positions.getZ(index[i + 2]));
|
|
154
|
-
triangles.push([a, b, c]);
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
157
|
-
// 无索引时,每三个顶点组成一个三角形
|
|
158
|
-
for (let i = 0; i < positions.count; i += 3) {
|
|
159
|
-
const a = new THREE.Vector3(positions.getX(i), positions.getY(i), positions.getZ(i));
|
|
160
|
-
const b = new THREE.Vector3(positions.getX(i + 1), positions.getY(i + 1), positions.getZ(i + 1));
|
|
161
|
-
const c = new THREE.Vector3(positions.getX(i + 2), positions.getY(i + 2), positions.getZ(i + 2));
|
|
162
|
-
triangles.push([a, b, c]);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
162
|
+
// 全部采样
|
|
163
|
+
sampleGlobal(geometry, maxPoints = 600) {
|
|
164
|
+
const triangles = this.extractTriangles(geometry);
|
|
166
165
|
if (triangles.length === 0) return [];
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const ab = new THREE.Vector3().subVectors(b, a);
|
|
171
|
-
const ac = new THREE.Vector3().subVectors(c, a);
|
|
166
|
+
const areas = triangles.map(t => {
|
|
167
|
+
const ab = new THREE.Vector3().subVectors(t.b, t.a);
|
|
168
|
+
const ac = new THREE.Vector3().subVectors(t.c, t.a);
|
|
172
169
|
return new THREE.Vector3().crossVectors(ab, ac).length() * 0.5;
|
|
173
170
|
});
|
|
174
|
-
const totalArea = areas.reduce((
|
|
175
|
-
|
|
176
|
-
// 按面积比例分配采样点数
|
|
171
|
+
const totalArea = areas.reduce((s, a) => s + a, 0);
|
|
177
172
|
const points = [];
|
|
178
173
|
for (let i = 0; i < triangles.length; i++) {
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
let
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
for (let j = 0; j < samplesForFace; j++) {
|
|
185
|
-
// 重心坐标随机采样
|
|
186
|
-
const u = Math.random();
|
|
187
|
-
const v = Math.random() * (1 - u);
|
|
188
|
-
const w = 1 - u - v;
|
|
189
|
-
const point = new THREE.Vector3()
|
|
190
|
-
.addScaledVector(a, u)
|
|
191
|
-
.addScaledVector(b, v)
|
|
192
|
-
.addScaledVector(c, w);
|
|
193
|
-
points.push(point);
|
|
174
|
+
const { a, b, c } = triangles[i];
|
|
175
|
+
const count = Math.max(1, Math.round((areas[i] / totalArea) * maxPoints));
|
|
176
|
+
for (let j = 0; j < count; j++) {
|
|
177
|
+
const u = Math.random(), v = Math.random() * (1 - u), w = 1 - u - v;
|
|
178
|
+
points.push(new THREE.Vector3().addScaledVector(a, u).addScaledVector(b, v).addScaledVector(c, w));
|
|
194
179
|
}
|
|
195
180
|
}
|
|
196
|
-
|
|
197
|
-
// 如果点数不够,随机补充
|
|
198
181
|
while (points.length < maxPoints) {
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
const v = Math.random() * (1 - u);
|
|
203
|
-
const w = 1 - u - v;
|
|
204
|
-
points.push(new THREE.Vector3().addScaledVector(a, u).addScaledVector(b, v).addScaledVector(c, w));
|
|
182
|
+
const t = triangles[Math.floor(Math.random() * triangles.length)];
|
|
183
|
+
const u = Math.random(), v = Math.random() * (1 - u), w = 1 - u - v;
|
|
184
|
+
points.push(new THREE.Vector3().addScaledVector(t.a, u).addScaledVector(t.b, v).addScaledVector(t.c, w));
|
|
205
185
|
}
|
|
206
186
|
return points.slice(0, maxPoints);
|
|
207
187
|
},
|
|
208
|
-
|
|
188
|
+
// 局部采样
|
|
189
|
+
sampleLocal(instancedMesh, instanceId, centerWorld, radius, pointsCount = 300) {
|
|
190
|
+
const worldMatrix = this.getInstanceWorldMatrix(instancedMesh, instanceId);
|
|
191
|
+
const invMatrix = new THREE.Matrix4().copy(worldMatrix).invert();
|
|
192
|
+
const centerLocal = centerWorld.clone().applyMatrix4(invMatrix);
|
|
193
|
+
const triangles = this.extractTriangles(instancedMesh.geometry);
|
|
194
|
+
if (triangles.length === 0) return [];
|
|
195
|
+
|
|
196
|
+
const relevantTris = triangles.filter(t => {
|
|
197
|
+
const box = new THREE.Box3().setFromPoints([t.a, t.b, t.c]);
|
|
198
|
+
return box.distanceToPoint(centerLocal) <= radius;
|
|
199
|
+
});
|
|
200
|
+
if (relevantTris.length === 0) return [];
|
|
201
|
+
|
|
202
|
+
const localPoints = [];
|
|
203
|
+
const perTri = Math.ceil(pointsCount / relevantTris.length);
|
|
204
|
+
for (const { a, b, c } of relevantTris) {
|
|
205
|
+
for (let j = 0; j < perTri; j++) {
|
|
206
|
+
const u = Math.random(), v = Math.random() * (1 - u), w = 1 - u - v;
|
|
207
|
+
const pt = new THREE.Vector3().addScaledVector(a, u).addScaledVector(b, v).addScaledVector(c, w);
|
|
208
|
+
if (pt.distanceTo(centerLocal) <= radius) localPoints.push(pt);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
while (localPoints.length < pointsCount && relevantTris.length > 0) {
|
|
212
|
+
const { a, b, c } = relevantTris[Math.floor(Math.random() * relevantTris.length)];
|
|
213
|
+
const u = Math.random(), v = Math.random() * (1 - u), w = 1 - u - v;
|
|
214
|
+
const pt = new THREE.Vector3().addScaledVector(a, u).addScaledVector(b, v).addScaledVector(c, w);
|
|
215
|
+
if (pt.distanceTo(centerLocal) <= radius) localPoints.push(pt);
|
|
216
|
+
}
|
|
217
|
+
return localPoints.map(p => p.clone().applyMatrix4(worldMatrix));
|
|
218
|
+
},
|
|
219
|
+
extractTriangles(geometry) {
|
|
209
220
|
const pos = geometry.getAttribute('position');
|
|
210
221
|
if (!pos) return [];
|
|
211
|
-
const
|
|
212
|
-
for (let i = 0; i < pos.count; i++) {
|
|
213
|
-
allVerts.push(new THREE.Vector3(pos.getX(i), pos.getY(i), pos.getZ(i)));
|
|
214
|
-
}
|
|
215
|
-
let sourceVerts = allVerts;
|
|
222
|
+
const triangles = [];
|
|
216
223
|
if (geometry.index) {
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
const idx = geometry.index.array;
|
|
225
|
+
for (let i = 0; i < idx.length; i += 3) {
|
|
226
|
+
const a = new THREE.Vector3(pos.getX(idx[i]), pos.getY(idx[i]), pos.getZ(idx[i]));
|
|
227
|
+
const b = new THREE.Vector3(pos.getX(idx[i + 1]), pos.getY(idx[i + 1]), pos.getZ(idx[i + 1]));
|
|
228
|
+
const c = new THREE.Vector3(pos.getX(idx[i + 2]), pos.getY(idx[i + 2]), pos.getZ(idx[i + 2]));
|
|
229
|
+
triangles.push({ a, b, c });
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
for (let i = 0; i < pos.count; i += 3) {
|
|
233
|
+
const a = new THREE.Vector3(pos.getX(i), pos.getY(i), pos.getZ(i));
|
|
234
|
+
const b = new THREE.Vector3(pos.getX(i + 1), pos.getY(i + 1), pos.getZ(i + 1));
|
|
235
|
+
const c = new THREE.Vector3(pos.getX(i + 2), pos.getY(i + 2), pos.getZ(i + 2));
|
|
236
|
+
triangles.push({ a, b, c });
|
|
227
237
|
}
|
|
228
238
|
}
|
|
229
|
-
|
|
230
|
-
const step = Math.ceil(sourceVerts.length / maxPoints);
|
|
231
|
-
const sampled = [];
|
|
232
|
-
for (let i = 0; i < sourceVerts.length; i += step) sampled.push(sourceVerts[i]);
|
|
233
|
-
sourceVerts = sampled;
|
|
234
|
-
}
|
|
235
|
-
return sourceVerts;
|
|
236
|
-
|
|
239
|
+
return triangles;
|
|
237
240
|
},
|
|
238
241
|
findClosestPair(points1, points2) {
|
|
239
242
|
let minDist = Infinity;
|
|
@@ -241,8 +244,8 @@ MeasureClearDistance.prototype = {
|
|
|
241
244
|
const c2 = new THREE.Vector3();
|
|
242
245
|
for (const p1 of points1) {
|
|
243
246
|
for (const p2 of points2) {
|
|
244
|
-
const d = p1.distanceTo(p2);
|
|
245
|
-
if (d
|
|
247
|
+
const d = Number((p1.distanceTo(p2)).toFixed(2));
|
|
248
|
+
if (d <= minDist) {
|
|
246
249
|
minDist = d;
|
|
247
250
|
c1.copy(p1);
|
|
248
251
|
c2.copy(p2);
|
|
@@ -251,38 +254,49 @@ MeasureClearDistance.prototype = {
|
|
|
251
254
|
}
|
|
252
255
|
return { point1: c1, point2: c2, distance: minDist };
|
|
253
256
|
},
|
|
254
|
-
|
|
255
|
-
// 平行判断 (基于包围盒主轴方向)
|
|
256
257
|
checkParallelism(inst1, id1, inst2, id2) {
|
|
258
|
+
// 1. 获取世界矩阵
|
|
257
259
|
const mat1 = this.getInstanceWorldMatrix(inst1, id1);
|
|
258
260
|
const mat2 = this.getInstanceWorldMatrix(inst2, id2);
|
|
259
|
-
const box1 = new THREE.Box3();
|
|
260
|
-
const box2 = new THREE.Box3();
|
|
261
|
-
|
|
262
|
-
// 确保包围盒存在
|
|
263
|
-
if (!inst1.geometry.boundingBox) inst1.geometry.computeBoundingBox();
|
|
264
|
-
if (!inst2.geometry.boundingBox) inst2.geometry.computeBoundingBox();
|
|
265
|
-
|
|
266
|
-
box1.copy(inst1.geometry.boundingBox).applyMatrix4(mat1);
|
|
267
|
-
box2.copy(inst2.geometry.boundingBox).applyMatrix4(mat2);
|
|
268
261
|
|
|
269
|
-
|
|
270
|
-
const
|
|
262
|
+
// 2. 提取纯旋转部分(消除缩放和位移)
|
|
263
|
+
const rotMat1 = new THREE.Matrix4().extractRotation(mat1);
|
|
264
|
+
const rotMat2 = new THREE.Matrix4().extractRotation(mat2);
|
|
271
265
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
266
|
+
// 如果物体可能有非均匀缩放,extractRotation 可能会出错,
|
|
267
|
+
// 更稳健的方法是手动归一化列向量
|
|
268
|
+
const normalizeMatrix = (m) => {
|
|
269
|
+
const col0 = new THREE.Vector3().setFromMatrixColumn(m, 0).normalize();
|
|
270
|
+
const col1 = new THREE.Vector3().setFromMatrixColumn(m, 1).normalize();
|
|
271
|
+
const col2 = new THREE.Vector3().setFromMatrixColumn(m, 2).normalize();
|
|
272
|
+
const normMat = new THREE.Matrix4();
|
|
273
|
+
normMat.makeBasis(col0, col1, col2);
|
|
274
|
+
return normMat;
|
|
276
275
|
};
|
|
277
|
-
const
|
|
278
|
-
const
|
|
276
|
+
const cleanRot1 = normalizeMatrix(rotMat1);
|
|
277
|
+
const cleanRot2 = normalizeMatrix(rotMat2);
|
|
279
278
|
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
// 3. 定义三个基向量(局部坐标系的X, Y, Z轴)
|
|
280
|
+
const axes = [
|
|
281
|
+
new THREE.Vector3(1, 0, 0),
|
|
282
|
+
new THREE.Vector3(0, 1, 0),
|
|
283
|
+
new THREE.Vector3(0, 0, 1)
|
|
284
|
+
];
|
|
284
285
|
|
|
285
|
-
|
|
286
|
+
// 4. 检查是否有任意一对基向量在世界空间中平行
|
|
287
|
+
const threshold = 0.99; // 提高阈值,更严格
|
|
288
|
+
for (const axis1 of axes) {
|
|
289
|
+
// 将局部基向量转换到世界方向
|
|
290
|
+
const worldDir1 = axis1.clone().applyMatrix4(cleanRot1).normalize();
|
|
291
|
+
for (const axis2 of axes) {
|
|
292
|
+
const worldDir2 = axis2.clone().applyMatrix4(cleanRot2).normalize();
|
|
293
|
+
const dot = Math.abs(worldDir1.dot(worldDir2));
|
|
294
|
+
if (dot > threshold) {
|
|
295
|
+
return true; // 只要有一对方向平行即判为平行
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
286
300
|
},
|
|
287
301
|
getInstanceWorldMatrix(instancedMesh, instanceId) {
|
|
288
302
|
const matrix = new THREE.Matrix4();
|
|
@@ -303,10 +317,22 @@ MeasureClearDistance.prototype = {
|
|
|
303
317
|
this.polyline.splice(0);
|
|
304
318
|
this.labels.splice(0);
|
|
305
319
|
this.firstTime = 0;
|
|
320
|
+
this.selectedObjects.forEach(item => {
|
|
321
|
+
let instanceIndex = item.instanceId
|
|
322
|
+
item.object.setColorAt(instanceIndex, item.object.material.userData.nColor);
|
|
323
|
+
item.object.instanceColor.needsUpdate = true;
|
|
324
|
+
})
|
|
325
|
+
this.selectedObjects.splice(0)
|
|
306
326
|
}
|
|
307
327
|
this.renderer.domElement.style.cursor = 'pointer';
|
|
308
328
|
},
|
|
309
329
|
clear() {
|
|
330
|
+
this.selectedObjects.forEach(item => {
|
|
331
|
+
let instanceIndex = item.instanceId
|
|
332
|
+
item.object.setColorAt(instanceIndex, item.object.material.userData.nColor);
|
|
333
|
+
item.object.instanceColor.needsUpdate = true;
|
|
334
|
+
})
|
|
335
|
+
this.selectedObjects.splice(0)
|
|
310
336
|
this.remove(this.points);
|
|
311
337
|
this.remove(this.polyline);
|
|
312
338
|
this.remove(this.labels);
|