three-cad-viewer 2.1.2 → 2.2.0

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/src/viewer.js CHANGED
@@ -79,6 +79,7 @@ class Viewer {
79
79
  this.orientationMarker = null;
80
80
  this.treeview = null;
81
81
  this.cadTools = new Tools(this);
82
+ this.newTreeBehavior = options.newTreeBehavior;
82
83
 
83
84
  this.ready = false;
84
85
  this.mixer = null;
@@ -144,7 +145,7 @@ class Viewer {
144
145
  * @param {DisplayOptions} options - The provided options object for the viewer.
145
146
  */
146
147
  setDisplayDefaults(options) {
147
- this.theme = "browser";
148
+ this.theme = "light";
148
149
  this.cadWidth = 800;
149
150
  this.treeWidth = 250;
150
151
  this.height = 600;
@@ -152,6 +153,7 @@ class Viewer {
152
153
  this.glass = false;
153
154
  this.tools = true;
154
155
  this.keymap = { shift: "shiftKey", ctrl: "ctrlKey", meta: "metaKey" };
156
+ this.newTreeBehavior = true;
155
157
 
156
158
  for (var option in options) {
157
159
  if (this[option] == null) {
@@ -160,6 +162,15 @@ class Viewer {
160
162
  this[option] = options[option];
161
163
  }
162
164
  }
165
+ if (
166
+ options.theme === "dark" ||
167
+ (options.theme == "browser" &&
168
+ window.matchMedia("(prefers-color-scheme: dark)").matches)
169
+ ) {
170
+ this.theme = "dark";
171
+ } else {
172
+ this.theme = "light";
173
+ }
163
174
  }
164
175
 
165
176
  /**
@@ -183,6 +194,13 @@ class Viewer {
183
194
  this[option] = options[option];
184
195
  }
185
196
  }
197
+
198
+ this.materialSettings = {
199
+ ambientIntensity: this.ambientIntensity,
200
+ directIntensity: this.directIntensity,
201
+ metalness: this.metalness,
202
+ roughness: this.roughness,
203
+ };
186
204
  }
187
205
 
188
206
  /**
@@ -201,6 +219,7 @@ class Viewer {
201
219
 
202
220
  this.clipIntersection = false;
203
221
  this.clipPlaneHelpers = false;
222
+ this.clipObjectColors = false;
204
223
  this.clipNormal0 = [-1, 0, 0];
205
224
  this.clipNormal1 = [0, -1, 0];
206
225
  this.clipNormal2 = [0, 0, -1];
@@ -210,7 +229,7 @@ class Viewer {
210
229
  this.control = "orbit";
211
230
  this.up = "Z";
212
231
  this.ticks = 10;
213
-
232
+ this.centerGrid = false;
214
233
  this.position = null;
215
234
  this.quaternion = null;
216
235
  this.target = null;
@@ -254,6 +273,7 @@ class Viewer {
254
273
  console.log("- blackEdges", this.blackEdges);
255
274
  console.log("- clipIntersection", this.clipIntersection);
256
275
  console.log("- clipPlaneHelpers", this.clipPlaneHelpers);
276
+ console.log("- clipObjectColors", this.clipObjectColors);
257
277
  console.log("- clipNormal0", this.clipNormal0);
258
278
  console.log("- clipNormal1", this.clipNormal1);
259
279
  console.log("- clipNormal2", this.clipNormal2);
@@ -347,16 +367,22 @@ class Viewer {
347
367
  // decompose faces
348
368
  var new_part = {
349
369
  parts: [],
350
- loc: [[0, 0, 0], [0, 0, 0, 1]],
370
+ loc: [
371
+ [0, 0, 0],
372
+ [0, 0, 0, 1],
373
+ ],
351
374
  name: "faces",
352
- id: `${part.id}/faces`
375
+ id: `${part.id}/faces`,
353
376
  };
354
377
  const vertices = shape.vertices;
355
378
  const normals = shape.normals;
356
379
  for (j = 0; j < shape.triangles.length; j++) {
357
380
  var triangles = shape.triangles[j];
358
381
  var new_shape = {
359
- loc: [[0, 0, 0], [0, 0, 0, 1]],
382
+ loc: [
383
+ [0, 0, 0],
384
+ [0, 0, 0, 1],
385
+ ],
360
386
  name: `faces_${j}`,
361
387
  id: `${part.id}/faces/faces_${j}`,
362
388
  type: "shapes",
@@ -369,10 +395,22 @@ class Viewer {
369
395
  subtype: part.subtype,
370
396
  shape: {
371
397
  triangles: [...Array(triangles.length).keys()],
372
- vertices: triangles.map((s) => [vertices[3 * s], vertices[3 * s + 1], vertices[3 * s + 2]]).flat(),
373
- normals: triangles.map((s) => [normals[3 * s], normals[3 * s + 1], normals[3 * s + 2]]).flat(),
374
- edges: []
375
- }
398
+ vertices: triangles
399
+ .map((s) => [
400
+ vertices[3 * s],
401
+ vertices[3 * s + 1],
402
+ vertices[3 * s + 2],
403
+ ])
404
+ .flat(),
405
+ normals: triangles
406
+ .map((s) => [
407
+ normals[3 * s],
408
+ normals[3 * s + 1],
409
+ normals[3 * s + 2],
410
+ ])
411
+ .flat(),
412
+ edges: [],
413
+ },
376
414
  };
377
415
  new_part.parts.push(new_shape);
378
416
  states[new_shape.id] = [1, 3];
@@ -385,25 +423,32 @@ class Viewer {
385
423
  // decompose edges
386
424
  new_part = {
387
425
  parts: [],
388
- loc: [[0, 0, 0], [0, 0, 0, 1]],
426
+ loc: [
427
+ [0, 0, 0],
428
+ [0, 0, 0, 1],
429
+ ],
389
430
  name: "edges",
390
431
  id: `${part.id}/edges`,
391
432
  };
392
- const multiColor = (Array.isArray(part.color) && (part.color.length == shape.edges.length));
433
+ const multiColor =
434
+ Array.isArray(part.color) && part.color.length == shape.edges.length;
393
435
  var color;
394
436
  for (j = 0; j < shape.edges.length; j++) {
395
437
  const edge = shape.edges[j];
396
438
  color = multiColor ? part.color[j] : part.color;
397
439
  new_shape = {
398
- loc: [[0, 0, 0], [0, 0, 0, 1]],
440
+ loc: [
441
+ [0, 0, 0],
442
+ [0, 0, 0, 1],
443
+ ],
399
444
  name: `edges_${j}`,
400
445
  id: `${part.id}/edges/edges_${j}`,
401
446
  type: "edges",
402
- color: (part.type == "shapes") ? this.edgeColor : color,
403
- width: (part.type == "shapes") ? 1 : part.width,
447
+ color: part.type == "shapes" ? this.edgeColor : color,
448
+ width: part.type == "shapes" ? 1 : part.width,
404
449
  bb: {},
405
450
  geomtype: shape.edge_types[j],
406
- shape: { "edges": edge }
451
+ shape: { edges: edge },
407
452
  };
408
453
  new_part.parts.push(new_shape);
409
454
  states[new_shape.id] = [3, 1];
@@ -415,21 +460,36 @@ class Viewer {
415
460
  // decompose vertices
416
461
  new_part = {
417
462
  parts: [],
418
- loc: [[0, 0, 0], [0, 0, 0, 1]],
463
+ loc: [
464
+ [0, 0, 0],
465
+ [0, 0, 0, 1],
466
+ ],
419
467
  name: "vertices",
420
468
  id: `${part.id}/vertices`,
421
469
  };
422
470
  var vertices = shape.obj_vertices;
423
471
  for (j = 0; j < vertices.length / 3; j++) {
424
472
  new_shape = {
425
- loc: [[0, 0, 0], [0, 0, 0, 1]],
473
+ loc: [
474
+ [0, 0, 0],
475
+ [0, 0, 0, 1],
476
+ ],
426
477
  name: `vertices${j}`,
427
478
  id: `${part.id}/vertices/vertices${j}`,
428
479
  type: "vertices",
429
- color: (part.type == "shapes" || part.type == "edges") ? this.edgeColor : part.color,
430
- size: (part.type == "shapes" || part.type == "edges") ? 4 : part.size,
480
+ color:
481
+ part.type == "shapes" || part.type == "edges"
482
+ ? this.edgeColor
483
+ : part.color,
484
+ size: part.type == "shapes" || part.type == "edges" ? 4 : part.size,
431
485
  bb: {},
432
- shape: { "obj_vertices": [vertices[3 * j], vertices[3 * j + 1], vertices[3 * j + 2]] }
486
+ shape: {
487
+ obj_vertices: [
488
+ vertices[3 * j],
489
+ vertices[3 * j + 1],
490
+ vertices[3 * j + 2],
491
+ ],
492
+ },
433
493
  };
434
494
  new_part.parts.push(new_shape);
435
495
  states[new_shape.id] = [3, 1];
@@ -445,7 +505,7 @@ class Viewer {
445
505
  delete states[part.id];
446
506
 
447
507
  return part;
448
- };
508
+ }
449
509
 
450
510
  /**
451
511
  * Render the shapes of the CAD object.
@@ -481,7 +541,7 @@ class Viewer {
481
541
  shape.edges = shape.edges.flat();
482
542
  } else if (part.type == "edges" || part.type == "shapes") {
483
543
  shape.edges = shape.edges.flat();
484
- };
544
+ }
485
545
  }
486
546
  }
487
547
  }
@@ -490,7 +550,7 @@ class Viewer {
490
550
  shapes = _render(shapes, states, options.measureTools);
491
551
  return [
492
552
  this._renderTessellatedShapes(shapes, states),
493
- this._getTree(shapes, states)
553
+ this._getTree(shapes, states),
494
554
  ];
495
555
  }
496
556
 
@@ -599,7 +659,6 @@ class Viewer {
599
659
  }
600
660
  };
601
661
 
602
-
603
662
  /**
604
663
  * Render scene and update orientation marker
605
664
  * If no animation loop exists, this needs to be called manually after every camera/scene change
@@ -609,7 +668,6 @@ class Viewer {
609
668
  * @param {boolean} notify - whether to send notification or not.
610
669
  */
611
670
  update = (updateMarker, notify = true) => {
612
-
613
671
  if (this.ready) {
614
672
  this.renderer.clear();
615
673
 
@@ -823,7 +881,6 @@ class Viewer {
823
881
  );
824
882
  timer.split("bounding box");
825
883
 
826
-
827
884
  //
828
885
  // add Info box
829
886
  //
@@ -854,7 +911,9 @@ class Viewer {
854
911
  10,
855
912
  -10,
856
913
  10,
857
- 0, 100);
914
+ 0,
915
+ 100,
916
+ );
858
917
  this.orthographicCamera.position.z = 50;
859
918
  this.orthographicCamera.up = this.camera.up;
860
919
 
@@ -923,9 +982,11 @@ class Viewer {
923
982
  this.display,
924
983
  this.bbox,
925
984
  this.ticks,
985
+ this.centerGrid,
926
986
  this.axes0,
927
987
  this.grid,
928
988
  options.up == "Z",
989
+ this.theme,
929
990
  );
930
991
  this.gridHelper.computeGrid();
931
992
 
@@ -951,39 +1012,62 @@ class Viewer {
951
1012
  );
952
1013
  this.scene.add(this.axesHelper);
953
1014
 
1015
+ // const geometry = new THREE.SphereGeometry(this.gridSize / 2, 32, 16);
1016
+ // const material = new THREE.MeshBasicMaterial({
1017
+ // color: 0xffff00,
1018
+ // opacity: 0.2,
1019
+ // transparent: true,
1020
+ // depthWrite: false,
1021
+ // });
1022
+ // const sphere = new THREE.Mesh(geometry, material);
1023
+ // const sgroup = new THREE.Group();
1024
+ // sgroup.add(sphere);
1025
+ // sgroup.position.set(...this.bbox.center());
1026
+ // this.scene.add(sgroup);
1027
+
954
1028
  //
955
1029
  // set up clipping planes and helpers
956
1030
  //
957
-
1031
+ const cSize =
1032
+ 1.1 *
1033
+ Math.max(
1034
+ Math.abs(this.bbox.min.length()),
1035
+ Math.abs(this.bbox.max.length()),
1036
+ );
958
1037
  this.clipping = new Clipping(
959
- this.bbox.center,
960
- this.gridSize,
961
- this.gridSize / 2,
962
- (index, normal) => this.display.setNormalLabel(index, normal),
1038
+ this.bbox.center(),
1039
+ 2 * cSize,
1040
+ this.nestedGroup,
1041
+ this.display,
963
1042
  this.theme,
964
1043
  );
965
1044
 
966
- this.display.setSliderLimits(this.gridSize / 2);
1045
+ this.display.setSliderLimits(this.gridSize / 2, this.bbox.center());
1046
+
1047
+ this.setClipNormal(0, options.clipNormal0, true);
1048
+ this.setClipNormal(1, options.clipNormal1, true);
1049
+ this.setClipNormal(2, options.clipNormal2, true);
1050
+
1051
+ this.clipSlider0 = (options.clipSlider0 != null) ? (options.clipSlider0) : this.gridSize / 2;
1052
+ this.clipSlider1 = (options.clipSlider1 != null) ? (options.clipSlider1) : this.gridSize / 2;
1053
+ this.clipSlider2 = (options.clipSlider2 != null) ? (options.clipSlider2) : this.gridSize / 2;
967
1054
 
968
- this.clipSlider0 = this.gridSize / 2;
969
- this.clipSlider1 = this.gridSize / 2;
970
- this.clipSlider2 = this.gridSize / 2;
971
1055
  this.setClipSlider(0, this.clipSlider0, true);
972
1056
  this.setClipSlider(1, this.clipSlider1, true);
973
1057
  this.setClipSlider(2, this.clipSlider2, true);
974
1058
 
975
- this.setClipNormal(0, options.clipNormal0, false);
976
- this.setClipNormal(1, options.clipNormal1, false);
977
- this.setClipNormal(2, options.clipNormal2, false);
978
1059
 
979
- this.setClipIntersection(options.clipIntersection, false);
980
- this.setClipPlaneHelpersCheck(options.clipPlaneHelpers);
1060
+ this.setClipIntersection(options.clipIntersection, true);
1061
+ this.setClipObjectColorCaps(options.clipObjectColors, true);
1062
+ this.setClipPlaneHelpersCheck(options.clipPlaneHelpers, true);
981
1063
 
982
1064
  this.scene.add(this.clipping.planeHelpers);
983
1065
  this.nestedGroup.setClipPlanes(this.clipping.clipPlanes);
984
1066
 
985
1067
  this.setLocalClipping(false); // only allow clipping when Clipping tab is selected
986
1068
 
1069
+ this.clipping.setVisible(false);
1070
+
987
1071
  this.display.metalnessSlider.setValue(this.metalness * 100);
988
1072
  this.display.roughnessSlider.setValue(this.roughness * 100);
989
1073
  this.display.ambientlightSlider.setValue(this.ambientIntensity * 100);
@@ -1019,6 +1103,7 @@ class Viewer {
1019
1103
  this.setObjects,
1020
1104
  this.handlePick,
1021
1105
  theme,
1106
+ this.newTreeBehavior,
1022
1107
  );
1023
1108
 
1024
1109
  this.display.addCadTree(this.treeview.render(options.collapse));
@@ -1385,7 +1470,11 @@ class Viewer {
1385
1470
  if (flag) {
1386
1471
  this.renderer.domElement.addEventListener("dblclick", this.pick, false);
1387
1472
  } else {
1388
- this.renderer.domElement.removeEventListener("dblclick", this.pick, false);
1473
+ this.renderer.domElement.removeEventListener(
1474
+ "dblclick",
1475
+ this.pick,
1476
+ false,
1477
+ );
1389
1478
  }
1390
1479
  }
1391
1480
 
@@ -1402,6 +1491,7 @@ class Viewer {
1402
1491
  this.height,
1403
1492
  this.bb_max / 30,
1404
1493
  this.scene.children.slice(0, 1),
1494
+ // eslint-disable-next-line no-unused-vars
1405
1495
  (ev) => { },
1406
1496
  );
1407
1497
  raycaster.init();
@@ -1430,17 +1520,15 @@ class Viewer {
1430
1520
  raycaster.dispose();
1431
1521
  };
1432
1522
 
1433
-
1434
1523
  //
1435
1524
  // Handle CAD Tools
1436
- //
1525
+ //
1437
1526
 
1438
1527
  clearSelection = () => {
1439
1528
  this.nestedGroup.clearSelection();
1440
1529
  this.cadTools.handleResetSelection();
1441
1530
  };
1442
1531
 
1443
-
1444
1532
  _releaseLastSelected = (clear) => {
1445
1533
  if (this.lastObject != null) {
1446
1534
  let objs = this.lastObject.objs();
@@ -1450,7 +1538,7 @@ class Viewer {
1450
1538
 
1451
1539
  if (clear) {
1452
1540
  this.lastObject = null;
1453
- };
1541
+ }
1454
1542
  }
1455
1543
  };
1456
1544
 
@@ -1498,7 +1586,9 @@ class Viewer {
1498
1586
  const objectGroup = object.object.parent;
1499
1587
  if (objectGroup !== this.lastObject) {
1500
1588
  this._releaseLastSelected(false);
1501
- const fromSolid = this.raycaster.filters.topoFilter.includes(TopoFilter.solid);
1589
+ const fromSolid = this.raycaster.filters.topoFilter.includes(
1590
+ TopoFilter.solid,
1591
+ );
1502
1592
 
1503
1593
  const pickedObj = new PickedObject(objectGroup, fromSolid);
1504
1594
  for (let obj of pickedObj.objs()) {
@@ -1547,11 +1637,10 @@ class Viewer {
1547
1637
  }
1548
1638
  };
1549
1639
 
1550
-
1551
1640
  /**
1552
1641
  * Handle a backend response sent by the backend
1553
1642
  * The response is a JSON object sent by the Python backend through VSCode
1554
- * @param {object} response
1643
+ * @param {object} response
1555
1644
  */
1556
1645
  handleBackendResponse = (response) => {
1557
1646
  if (response.subtype === "tool_response") {
@@ -1559,7 +1648,6 @@ class Viewer {
1559
1648
  }
1560
1649
  };
1561
1650
 
1562
-
1563
1651
  //
1564
1652
  // Getters and Setters
1565
1653
  //
@@ -1594,8 +1682,8 @@ class Viewer {
1594
1682
  * @param {string} action - one of "grid" (all grids), "grid-xy","grid-xz", "grid-yz"
1595
1683
  * @param {boolean} [notify=true] - whether to send notification or not.
1596
1684
  */
1597
- setGrid = (action, notify = true) => {
1598
- this.gridHelper.setGrid(action);
1685
+ setGrid = (action, flag, notify = true) => {
1686
+ this.gridHelper.setGrid(action, flag);
1599
1687
 
1600
1688
  this.checkChanges({ grid: this.gridHelper.grid }, notify);
1601
1689
 
@@ -1671,6 +1759,20 @@ class Viewer {
1671
1759
  this.update(this.updateMarker);
1672
1760
  };
1673
1761
 
1762
+ resetMaterial = () => {
1763
+ this.setMetalness(this.materialSettings.metalness, true);
1764
+ this.display.setMetalness(this.materialSettings.metalness);
1765
+
1766
+ this.setRoughness(this.materialSettings.roughness, true);
1767
+ this.display.setRoughness(this.materialSettings.roughness);
1768
+
1769
+ this.setAmbientLight(this.materialSettings.ambientIntensity, true);
1770
+ this.display.setAmbientLight(this.materialSettings.ambientIntensity);
1771
+
1772
+ this.setDirectLight(this.materialSettings.directIntensity, true);
1773
+ this.display.setDirectLight(this.materialSettings.directIntensity);
1774
+ };
1775
+
1674
1776
  /**
1675
1777
  * Get transparency state of CAD objects.
1676
1778
  * @returns {boolean} transparent value.
@@ -2051,11 +2153,63 @@ class Viewer {
2051
2153
  this.nestedGroup.setClipIntersection(flag);
2052
2154
  this.display.setClipIntersectionCheck(flag);
2053
2155
 
2156
+ for (var child of this.nestedGroup.rootGroup.children) {
2157
+ if (child.name == "PlaneMeshes") {
2158
+ for (var capPlane of child.children) {
2159
+ if (flag) {
2160
+ capPlane.material.clippingPlanes =
2161
+ this.clipping.reverseClipPlanes.filter((_, j) => j !== capPlane.index);
2162
+ } else {
2163
+ capPlane.material.clippingPlanes = this.clipping.clipPlanes.filter(
2164
+ (_, j) => j !== capPlane.index,
2165
+ );
2166
+ }
2167
+ }
2168
+ }
2169
+ }
2170
+
2171
+ for (child of this.scene.children) {
2172
+ if (child.name == "PlaneHelpers") {
2173
+ for (var helper of child.children) {
2174
+ if (flag) {
2175
+ helper.material.clippingPlanes =
2176
+ this.clipping.reverseClipPlanes.filter((_, j) => j !== helper.index);
2177
+ } else {
2178
+ helper.material.clippingPlanes = this.clipping.clipPlanes.filter(
2179
+ (_, j) => j !== helper.index,
2180
+ );
2181
+ }
2182
+ }
2183
+ }
2184
+ }
2185
+
2054
2186
  this.checkChanges({ clip_intersection: flag }, notify);
2055
2187
 
2056
2188
  this.update(this.updateMarker);
2057
2189
  };
2058
2190
 
2191
+ /**
2192
+ * Get whether the clipping caps color status
2193
+ * @returns {boolean} color caps value (object color (true) or RGB (false)).
2194
+ */
2195
+ getObjectColorCaps = () => {
2196
+ return this.clipping.getObjectColorCaps();
2197
+ };
2198
+
2199
+ /**
2200
+ * Toggle the clipping caps color between object color and RGB
2201
+ * @function
2202
+ * @param {boolean} flag - whether to use intersection mode
2203
+ * @param {boolean} [notify=true] - whether to send notification or not.
2204
+ */
2205
+ setClipObjectColorCaps = (flag, notify = true) => {
2206
+ if (flag == null) return;
2207
+ this.clipping.setObjectColorCaps(flag);
2208
+ this.display.setClipObjectColorsCheck(flag);
2209
+ this.checkChanges({ clip_object_colors: flag }, notify);
2210
+ this.update(this.updateMarker);
2211
+ };
2212
+
2059
2213
  /**
2060
2214
  * Get clipping plane state.
2061
2215
  * @returns {boolean} clip plane visibility value.
@@ -2111,12 +2265,14 @@ class Viewer {
2111
2265
  */
2112
2266
  setClipNormal(index, normal, notify = true) {
2113
2267
  if (normal == null) return;
2268
+ const normal1 = new THREE.Vector3(...normal).normalize().toArray();
2269
+ this.clipNormals[index] = normal1;
2114
2270
 
2115
- this.clipNormals[index] = normal;
2116
-
2117
- this.clipping.setNormal(index, new THREE.Vector3(...normal));
2271
+ this.clipping.setNormal(index, new THREE.Vector3(...normal1));
2272
+ this.clipping.setConstant(index, this.gridSize / 2);
2273
+ this.setClipSlider(index, this.gridSize / 2);
2118
2274
  var notifyObject = {};
2119
- notifyObject[`clip_normal_${index}`] = normal;
2275
+ notifyObject[`clip_normal_${index}`] = normal1;
2120
2276
 
2121
2277
  this.checkChanges(notifyObject, notify);
2122
2278