@xeokit/xeokit-sdk 2.6.94 → 2.6.95
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/xeokit-sdk.cjs.js +4380 -4578
- package/dist/xeokit-sdk.es.js +4380 -4578
- package/dist/xeokit-sdk.es5.js +716 -746
- package/dist/xeokit-sdk.min.cjs.js +8 -8
- package/dist/xeokit-sdk.min.es.js +8 -8
- package/dist/xeokit-sdk.min.es5.js +7 -7
- package/package.json +1 -1
- package/src/viewer/scene/math/math.js +310 -1
- package/src/viewer/scene/model/SceneModelEntity.js +6 -3
- package/src/viewer/scene/scene/Scene.js +0 -4
- package/src/viewer/scene/sectionCaps/SectionCaps.js +136 -642
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xeokit/xeokit-sdk",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.95",
|
|
4
4
|
"description": "3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision",
|
|
5
5
|
"module": "./dist/xeokit-sdk.es.js",
|
|
6
6
|
"main": "./dist/xeokit-sdk.cjs.js",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Some temporary vars to help avoid garbage collection
|
|
2
2
|
|
|
3
|
+
import earcut from '../libs/earcut.js';
|
|
4
|
+
|
|
3
5
|
let doublePrecision = true;
|
|
4
6
|
let FloatArrayType = doublePrecision ? Float64Array : Float32Array;
|
|
5
7
|
|
|
@@ -5411,6 +5413,313 @@ math.planeClipsPositions3 = function (pos, dir, positions, numElementsPerPositio
|
|
|
5411
5413
|
}
|
|
5412
5414
|
}
|
|
5413
5415
|
return false;
|
|
5414
|
-
}
|
|
5416
|
+
};
|
|
5417
|
+
|
|
5418
|
+
/**
|
|
5419
|
+
* Tests whether a plane intersects a bouding box.
|
|
5420
|
+
* @param plane SectionPlane
|
|
5421
|
+
* @param aabb Bounding box
|
|
5422
|
+
* @returns {boolean}
|
|
5423
|
+
*/
|
|
5424
|
+
math.planeIntersectsAABB3 = (plane, aabb) => {
|
|
5425
|
+
const min = [aabb[0], aabb[1], aabb[2]];
|
|
5426
|
+
const max = [aabb[3], aabb[4], aabb[5]];
|
|
5427
|
+
|
|
5428
|
+
const corners = [
|
|
5429
|
+
[min[0], min[1], min[2]], // 000
|
|
5430
|
+
[max[0], min[1], min[2]], // 100
|
|
5431
|
+
[min[0], max[1], min[2]], // 010
|
|
5432
|
+
[max[0], max[1], min[2]], // 110
|
|
5433
|
+
[min[0], min[1], max[2]], // 001
|
|
5434
|
+
[max[0], min[1], max[2]], // 101
|
|
5435
|
+
[min[0], max[1], max[2]], // 011
|
|
5436
|
+
[max[0], max[1], max[2]] // 111
|
|
5437
|
+
];
|
|
5438
|
+
|
|
5439
|
+
// Calculate distance from each corner to the plane
|
|
5440
|
+
let hasPositive = false;
|
|
5441
|
+
let hasNegative = false;
|
|
5442
|
+
|
|
5443
|
+
for (const corner of corners) {
|
|
5444
|
+
const distance = plane.dist + math.dotVec3(plane.dir, corner);
|
|
5445
|
+
|
|
5446
|
+
if (distance > 0) hasPositive = true;
|
|
5447
|
+
if (distance < 0) hasNegative = true;
|
|
5448
|
+
|
|
5449
|
+
// If we found points on both sides, the plane intersects the box
|
|
5450
|
+
if (hasPositive && hasNegative) return true;
|
|
5451
|
+
}
|
|
5452
|
+
|
|
5453
|
+
// If all points are on the same side, no intersection
|
|
5454
|
+
return false;
|
|
5455
|
+
};
|
|
5456
|
+
|
|
5457
|
+
/**
|
|
5458
|
+
* makeSectionPlaneSlicer returns a function that slices a given geometry with a given SectionPlane
|
|
5459
|
+
* The implementation finds segments where the SectionPlane intersects with the geometry's surface.
|
|
5460
|
+
* These segments form loops that are triangulated using the Earcut algorithm, taking internal holes into account.
|
|
5461
|
+
* @param {Number[]} pos SectionPlane position 3D vector
|
|
5462
|
+
* @param {Number[]} dir SectionPlane rotation quaternion
|
|
5463
|
+
* @returns {function}
|
|
5464
|
+
*/
|
|
5465
|
+
math.makeSectionPlaneSlicer = (function() {
|
|
5466
|
+
|
|
5467
|
+
const tempVec2a = math.vec2();
|
|
5468
|
+
const tempVec2b = math.vec2();
|
|
5469
|
+
const tempVec2c = math.vec2();
|
|
5470
|
+
const tempVec2d = math.vec2();
|
|
5471
|
+
|
|
5472
|
+
const tempVec3a = math.vec3();
|
|
5473
|
+
const tempVec3b = math.vec3();
|
|
5474
|
+
const tempVec3c = math.vec3();
|
|
5475
|
+
const tempVec3d = math.vec3();
|
|
5476
|
+
|
|
5477
|
+
const triangle = [ math.vec3(), math.vec3(), math.vec3() ];
|
|
5478
|
+
|
|
5479
|
+
const worldUp = [0, 1, 0];
|
|
5480
|
+
const worldRight = [1, 0, 0];
|
|
5481
|
+
const worldForward = [0, 0, 1];
|
|
5482
|
+
|
|
5483
|
+
const sqDistVec3 = (function() {
|
|
5484
|
+
const tmp = math.vec3();
|
|
5485
|
+
return (a, b) => math.sqLenVec3(math.subVec3(a, b, tmp));
|
|
5486
|
+
})();
|
|
5487
|
+
|
|
5488
|
+
const iota = n => {
|
|
5489
|
+
const ret = [ ];
|
|
5490
|
+
for (let i = 0; i < n; ++i)
|
|
5491
|
+
ret.push(i);
|
|
5492
|
+
return ret;
|
|
5493
|
+
};
|
|
5494
|
+
|
|
5495
|
+
const setCoord2D = (p, dst) => { dst[0] = p.coord2Dx; dst[1] = p.coord2Dy; return dst; };
|
|
5496
|
+
|
|
5497
|
+
const ccw = (a, b, c) => ((c[1] - a[1]) * (b[0] - a[0])) > ((b[1] - a[1]) * (c[0] - a[0]));
|
|
5498
|
+
|
|
5499
|
+
const isLoopInside = (inner, outer) => {
|
|
5500
|
+
const bbI = inner.boundingBox;
|
|
5501
|
+
const bbO = outer.boundingBox;
|
|
5502
|
+
if ((bbI[0] < bbO[0]) || (bbI[1] < bbO[1]) || (bbI[2] > bbO[2]) || (bbI[3] > bbO[3])) {
|
|
5503
|
+
return false;
|
|
5504
|
+
}
|
|
5505
|
+
|
|
5506
|
+
const innerEndpoints = inner.endPoints;
|
|
5507
|
+
const outerEndpoints = outer.endPoints;
|
|
5508
|
+
for (let ii = 0, ij = innerEndpoints.length - 1; ii < innerEndpoints.length; ij = ii++) {
|
|
5509
|
+
const i0 = setCoord2D(innerEndpoints[ii], tempVec2a);
|
|
5510
|
+
const [i0x, i0y] = i0;
|
|
5511
|
+
const i1 = setCoord2D(innerEndpoints[ij], tempVec2b);
|
|
5512
|
+
|
|
5513
|
+
let inside = false;
|
|
5514
|
+
for (let i = 0, j = outerEndpoints.length - 1; i < outerEndpoints.length; j = i++) {
|
|
5515
|
+
const o0 = setCoord2D(outerEndpoints[i], tempVec2c);
|
|
5516
|
+
const o1 = setCoord2D(outerEndpoints[j], tempVec2d);
|
|
5517
|
+
|
|
5518
|
+
if ((ccw(i0, o0, o1) !== ccw(i1, o0, o1)) && (ccw(i0, i1, o0) !== ccw(i0, i1, o1))) {
|
|
5519
|
+
return false; // segments intersect
|
|
5520
|
+
}
|
|
5521
|
+
|
|
5522
|
+
const [o0x, o0y] = o0;
|
|
5523
|
+
const [o1x, o1y] = o1;
|
|
5524
|
+
|
|
5525
|
+
const dx = i0x - o0x;
|
|
5526
|
+
const dy = i0y - o0y;
|
|
5527
|
+
|
|
5528
|
+
const oDx = o1x - o0x;
|
|
5529
|
+
const oDy = o1y - o0y;
|
|
5530
|
+
|
|
5531
|
+
const dot = (((oDx !== 0) || (oDy !== 0)) && (Math.abs(oDx * dy - oDy * dx) < 1e-10)) ? (dx * oDx + dy * oDy) : -1;
|
|
5532
|
+
if ((dot >= 0) && (dot <= (Math.pow(oDx, 2) + Math.pow(oDy, 2)))) {
|
|
5533
|
+
return false; // on edge
|
|
5534
|
+
}
|
|
5535
|
+
|
|
5536
|
+
if (((o0y > i0y) !== (o1y > i0y)) && (dx < (dy * oDx / oDy))) {
|
|
5537
|
+
inside = !inside;
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
if (! inside) {
|
|
5541
|
+
return false;
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
|
|
5545
|
+
return true;
|
|
5546
|
+
};
|
|
5547
|
+
|
|
5548
|
+
return function(planePos, planeRot) {
|
|
5549
|
+
const planeU = math.vec3ApplyQuaternion(planeRot, worldRight, math.vec3());
|
|
5550
|
+
const planeV = math.vec3ApplyQuaternion(planeRot, worldUp, math.vec3());
|
|
5551
|
+
const planeN = math.vec3ApplyQuaternion(planeRot, worldForward, math.vec3());
|
|
5552
|
+
|
|
5553
|
+
return function(meshCenter, meshIndices, meshPositions) {
|
|
5554
|
+
const planeToMesh = math.subVec3(meshCenter, planePos, math.vec3());
|
|
5555
|
+
const planeDist = math.dotVec3(planeN, planeToMesh);
|
|
5556
|
+
|
|
5557
|
+
const unsortedSegment = [ ];
|
|
5558
|
+
const indexedPositions = [ null ]; // to never return 0 from addPosition, so its result can be used as a predicate
|
|
5559
|
+
const addPosition = p => { const idx = indexedPositions.length; indexedPositions.push(math.vec3(p)); return idx; };
|
|
5560
|
+
|
|
5561
|
+
const setVertex = (i, dst) => {
|
|
5562
|
+
const idx = meshIndices[i] * 3;
|
|
5563
|
+
dst[0] = meshPositions[idx + 0];
|
|
5564
|
+
dst[1] = meshPositions[idx + 1];
|
|
5565
|
+
dst[2] = meshPositions[idx + 2];
|
|
5566
|
+
return dst;
|
|
5567
|
+
};
|
|
5568
|
+
|
|
5569
|
+
for (let meshIdx = 0; meshIdx < meshIndices.length; meshIdx += 3) {
|
|
5570
|
+
const p0 = setVertex(meshIdx + 0, triangle[0]);
|
|
5571
|
+
const p1 = setVertex(meshIdx + 1, triangle[1]);
|
|
5572
|
+
const p2 = setVertex(meshIdx + 2, triangle[2]);
|
|
5573
|
+
|
|
5574
|
+
if (math.compareVec3(p0, p1) || math.compareVec3(p1, p2) || math.compareVec3(p2, p0)) {
|
|
5575
|
+
continue; // skip degenerate triangle
|
|
5576
|
+
}
|
|
5577
|
+
|
|
5578
|
+
const d0 = planeDist + math.dotVec3(planeN, p0);
|
|
5579
|
+
const d1 = planeDist + math.dotVec3(planeN, p1);
|
|
5580
|
+
const d2 = planeDist + math.dotVec3(planeN, p2);
|
|
5581
|
+
|
|
5582
|
+
if ((d0 !== 0) || (d1 !== 0) || (d2 !== 0)) {
|
|
5583
|
+
const i0 = (d0 * d1 <= 0) && addPosition(math.lerpVec3(d0 / (d0 - d1), 0, 1, p0, p1, tempVec3a));
|
|
5584
|
+
const i1 = (d1 * d2 <= 0) && addPosition(math.lerpVec3(d1 / (d1 - d2), 0, 1, p1, p2, tempVec3a));
|
|
5585
|
+
const i2 = (d2 * d0 <= 0) && addPosition(math.lerpVec3(d2 / (d2 - d0), 0, 1, p2, p0, tempVec3a));
|
|
5586
|
+
|
|
5587
|
+
if (i0 ? (i1 || i2) : (i1 && i2)) { // triangle intersected by the section plane
|
|
5588
|
+
unsortedSegment.push(i0 ? [ i0, i1 || i2 ] : [ i1, i2 ]);
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5593
|
+
const endpointLoops = [ ];
|
|
5594
|
+
while (unsortedSegment.length > 0) {
|
|
5595
|
+
endpointLoops.push([ unsortedSegment[0][0], unsortedSegment[0][1] ]);
|
|
5596
|
+
const curEndpoints = endpointLoops[endpointLoops.length - 1];
|
|
5597
|
+
unsortedSegment.splice(0, 1);
|
|
5598
|
+
while (unsortedSegment.length > 0) {
|
|
5599
|
+
const lastPoint = indexedPositions[curEndpoints[curEndpoints.length - 1]];
|
|
5600
|
+
const closest = { distSq: sqDistVec3(indexedPositions[curEndpoints[0]], lastPoint), idx: -1, side: -1 };
|
|
5601
|
+
unsortedSegment.forEach((seg, i) => {
|
|
5602
|
+
const distSq0 = sqDistVec3(indexedPositions[seg[0]], lastPoint);
|
|
5603
|
+
const distSq1 = sqDistVec3(indexedPositions[seg[1]], lastPoint);
|
|
5604
|
+
const distSq = Math.min(distSq0, distSq1);
|
|
5605
|
+
if (closest.distSq > distSq) {
|
|
5606
|
+
closest.distSq = distSq;
|
|
5607
|
+
closest.idx = i;
|
|
5608
|
+
closest.side = (distSq1 < distSq0) ? 1 : 0;
|
|
5609
|
+
}
|
|
5610
|
+
});
|
|
5611
|
+
|
|
5612
|
+
if (closest.distSq < 1e-20) {
|
|
5613
|
+
const nextSegment = (closest.idx >= 0) && unsortedSegment[closest.idx];
|
|
5614
|
+
indexedPositions[nextSegment ? nextSegment[closest.side] : curEndpoints[0]] = lastPoint; // move the split face's (above) vertex to lastPoint, to not introduce gaps
|
|
5615
|
+
if (nextSegment) {
|
|
5616
|
+
unsortedSegment.splice(closest.idx, 1);
|
|
5617
|
+
const nextEnd = nextSegment[1 - closest.side];
|
|
5618
|
+
if (sqDistVec3(indexedPositions[nextEnd], indexedPositions[curEndpoints[0]]) > 1e-20) {
|
|
5619
|
+
curEndpoints.push(nextEnd);
|
|
5620
|
+
} else {
|
|
5621
|
+
break;
|
|
5622
|
+
}
|
|
5623
|
+
} else {
|
|
5624
|
+
break;
|
|
5625
|
+
}
|
|
5626
|
+
} else {
|
|
5627
|
+
endpointLoops.pop(); // Could not find a matching segment. Discard a loop that cannot be closed.
|
|
5628
|
+
break;
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
|
|
5633
|
+
const loops = endpointLoops.filter(endPoints => endPoints.length > 2).map((endPoints, idx) => {
|
|
5634
|
+
const planeEndpoints = endPoints.map(posIdx => {
|
|
5635
|
+
const P = math.addVec3(planeToMesh, indexedPositions[posIdx], tempVec3a);
|
|
5636
|
+
return { posIdx: posIdx, coord2Dx: math.dotVec3(planeU, P), coord2Dy: math.dotVec3(planeV, P) };
|
|
5637
|
+
});
|
|
5638
|
+
let doubleArea = 0;
|
|
5639
|
+
const aabb = math.collapseAABB2(math.AABB2());
|
|
5640
|
+
for (let i = 0; i < planeEndpoints.length; i++) {
|
|
5641
|
+
const p0 = planeEndpoints[i];
|
|
5642
|
+
tempVec2a[0] = p0.coord2Dx;
|
|
5643
|
+
tempVec2a[1] = p0.coord2Dy;
|
|
5644
|
+
math.expandAABB2Point2(aabb, tempVec2a);
|
|
5645
|
+
const p1 = planeEndpoints[(i + 1) % planeEndpoints.length];
|
|
5646
|
+
doubleArea += (p0.coord2Dx * p1.coord2Dy - p1.coord2Dx * p0.coord2Dy);
|
|
5647
|
+
}
|
|
5648
|
+
return {
|
|
5649
|
+
boundingBox: aabb,
|
|
5650
|
+
doubleArea: Math.abs(doubleArea),
|
|
5651
|
+
endPoints: planeEndpoints
|
|
5652
|
+
};
|
|
5653
|
+
}).sort((a, b) => b.doubleArea - a.doubleArea);
|
|
5654
|
+
|
|
5655
|
+
const sliceGeometries = [ ];
|
|
5656
|
+
|
|
5657
|
+
while (loops.length > 0) {
|
|
5658
|
+
const vertices2D = [ ];
|
|
5659
|
+
const vertices3D = [ ];
|
|
5660
|
+
|
|
5661
|
+
const appendLoopVertices = loop => loop.endPoints.forEach(endpoint2D => {
|
|
5662
|
+
vertices2D.push(endpoint2D.coord2Dx, endpoint2D.coord2Dy);
|
|
5663
|
+
vertices3D.push(endpoint2D.posIdx);
|
|
5664
|
+
});
|
|
5665
|
+
|
|
5666
|
+
const outerLoop = loops.shift();
|
|
5667
|
+
appendLoopVertices(outerLoop);
|
|
5668
|
+
|
|
5669
|
+
const innerLoops = [ ];
|
|
5670
|
+
let innerLoopIdx = 0;
|
|
5671
|
+
while (innerLoopIdx < loops.length) {
|
|
5672
|
+
const loop = loops[innerLoopIdx];
|
|
5673
|
+
if (isLoopInside(loop, outerLoop) && innerLoops.every(inner => !isLoopInside(loop, inner))) {
|
|
5674
|
+
loop.index = vertices2D.length / 2;
|
|
5675
|
+
appendLoopVertices(loop);
|
|
5676
|
+
innerLoops.push(loop);
|
|
5677
|
+
loops.splice(innerLoopIdx, 1);
|
|
5678
|
+
} else {
|
|
5679
|
+
++innerLoopIdx;
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
|
|
5683
|
+
// Triangulate
|
|
5684
|
+
const triangles = earcut(vertices2D, innerLoops.map(loop => loop.index));
|
|
5685
|
+
|
|
5686
|
+
const positions = [ ];
|
|
5687
|
+
const normals = [ ];
|
|
5688
|
+
const uv = [ ];
|
|
5689
|
+
for (let i = 0; i < triangles.length; i += 3) {
|
|
5690
|
+
const v0 = indexedPositions[vertices3D[triangles[i + 0]]];
|
|
5691
|
+
const v1 = indexedPositions[vertices3D[triangles[i + 1]]];
|
|
5692
|
+
const v2 = indexedPositions[vertices3D[triangles[i + 2]]];
|
|
5693
|
+
math.subVec3(v1, v0, tempVec3b);
|
|
5694
|
+
math.subVec3(v2, v0, tempVec3c);
|
|
5695
|
+
math.normalizeVec3(math.cross3Vec3(tempVec3b, tempVec3c, tempVec3c), tempVec3c);
|
|
5696
|
+
const facedPositively = math.dotVec3(tempVec3c, planeN) <= 0;
|
|
5697
|
+
if (! facedPositively) {
|
|
5698
|
+
math.negateVec3(tempVec3c, tempVec3c);
|
|
5699
|
+
}
|
|
5700
|
+
for (let j = 0; j < 3; ++j) {
|
|
5701
|
+
const vIdx = triangles[i + (facedPositively ? j : (2 - j))];
|
|
5702
|
+
const v = indexedPositions[vertices3D[vIdx]];
|
|
5703
|
+
positions.push(v[0], v[1], v[2]);
|
|
5704
|
+
normals.push(tempVec3c[0], tempVec3c[1], tempVec3c[2]);
|
|
5705
|
+
const uvOff = 2 * vIdx;
|
|
5706
|
+
uv.push(-vertices2D[uvOff], vertices2D[uvOff + 1]);
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
|
|
5710
|
+
if (positions.length > 0) {
|
|
5711
|
+
sliceGeometries.push({
|
|
5712
|
+
indices: iota(positions.length / 3),
|
|
5713
|
+
positions: positions,
|
|
5714
|
+
normals: normals,
|
|
5715
|
+
uv: uv
|
|
5716
|
+
});
|
|
5717
|
+
}
|
|
5718
|
+
}
|
|
5719
|
+
|
|
5720
|
+
return sliceGeometries;
|
|
5721
|
+
};
|
|
5722
|
+
};
|
|
5723
|
+
})();
|
|
5415
5724
|
|
|
5416
5725
|
export {math};
|
|
@@ -659,9 +659,12 @@ export class SceneModelEntity {
|
|
|
659
659
|
* @type {Material}
|
|
660
660
|
*/
|
|
661
661
|
set capMaterial(value) {
|
|
662
|
-
if(
|
|
663
|
-
|
|
664
|
-
|
|
662
|
+
if (this.scene.readableGeometryEnabled) {
|
|
663
|
+
this._capMaterial = value;
|
|
664
|
+
this.scene._sectionCaps._onCapMaterialUpdated(this);
|
|
665
|
+
} else {
|
|
666
|
+
throw "The `capMaterial` assignment requires `Viewer::readableGeometryEnabled` to be `true`";
|
|
667
|
+
}
|
|
665
668
|
}
|
|
666
669
|
|
|
667
670
|
/**
|
|
@@ -994,10 +994,6 @@ class Scene extends Component {
|
|
|
994
994
|
// Scene. Violates Hollywood Principle, where we could just filter on type in _addComponent,
|
|
995
995
|
// but this is faster than checking the type of each component in such a filter.
|
|
996
996
|
|
|
997
|
-
_capMaterialUpdated(entityId, modelId) {
|
|
998
|
-
this._sectionCaps._onCapMaterialUpdated(entityId, modelId);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
997
|
_sectionPlaneCreated(sectionPlane) {
|
|
1002
998
|
this.sectionPlanes[sectionPlane.id] = sectionPlane;
|
|
1003
999
|
this.scene._sectionPlanesState.addSectionPlane(sectionPlane._state);
|