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/clipping.js CHANGED
@@ -1,124 +1,309 @@
1
1
  import * as THREE from "three";
2
+ import { ObjectGroup } from "./objectgroup.js";
2
3
 
3
- class PlaneHelper extends THREE.Line {
4
- constructor(index, plane, center, size = 1, hex = 0xffff00) {
5
- const color = hex;
4
+ const normals = [
5
+ new THREE.Vector3(-1, 0, 0),
6
+ new THREE.Vector3(0, -1, 0),
7
+ new THREE.Vector3(0, 0, -1),
8
+ ];
6
9
 
7
- const positions = [-1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1];
10
+ const planeColors = {
11
+ light: [0xff0000, 0x00ff00, 0x0000ff],
12
+ dark: [0xff4500, 0x32cd32, 0x3b9eff],
13
+ };
8
14
 
9
- const geometry = new THREE.BufferGeometry();
10
- geometry.setAttribute(
11
- "position",
12
- new THREE.Float32BufferAttribute(positions, 3),
13
- );
14
- geometry.computeBoundingSphere();
15
+ const planeHelperMaterial = new THREE.MeshBasicMaterial({
16
+ opacity: 0.1,
17
+ transparent: true,
18
+ depthWrite: false,
19
+ toneMapped: false,
20
+ side: THREE.DoubleSide,
21
+ });
15
22
 
16
- super(
17
- geometry,
18
- new THREE.LineBasicMaterial({ color: color, toneMapped: false }),
19
- );
23
+ // everywhere that the back faces are visible (clipped region) the stencil
24
+ // buffer is incremented by 1.
25
+ const backStencilMaterial = new THREE.MeshBasicMaterial({
26
+ depthWrite: false,
27
+ depthTest: false,
28
+ colorWrite: false,
29
+ side: THREE.BackSide,
20
30
 
21
- this.type = "PlaneHelper";
22
- this.index = index;
31
+ stencilWrite: true,
32
+ stencilFunc: THREE.AlwaysStencilFunc,
33
+ stencilFail: THREE.IncrementWrapStencilOp,
34
+ stencilZFail: THREE.IncrementWrapStencilOp,
35
+ stencilZPass: THREE.IncrementWrapStencilOp,
36
+ });
37
+
38
+ // everywhere that the front faces are visible the stencil
39
+ // buffer is decremented back to 0.
40
+ const frontStencilMaterial = new THREE.MeshBasicMaterial({
41
+ depthWrite: false,
42
+ depthTest: false,
43
+ colorWrite: false,
44
+ side: THREE.FrontSide,
45
+
46
+ stencilWrite: true,
47
+ stencilFunc: THREE.AlwaysStencilFunc,
48
+ stencilFail: THREE.DecrementWrapStencilOp,
49
+ stencilZFail: THREE.DecrementWrapStencilOp,
50
+ stencilZPass: THREE.DecrementWrapStencilOp,
51
+ });
52
+
53
+ // draw the plane everywhere that the stencil buffer != 0, which will
54
+ // only be in the clipped region where back faces are visible.
55
+ const stencilPlaneMaterial = new THREE.MeshStandardMaterial({
56
+ metalness: 0.3,
57
+ roughness: 0.65,
58
+ opacity: 1.0,
59
+ transparent: false,
60
+ side: THREE.DoubleSide,
61
+ polygonOffset: true,
62
+ polygonOffsetFactor: 1.0,
63
+ polygonOffsetUnits: 1.0,
64
+
65
+ stencilWrite: true,
66
+ stencilRef: 0,
67
+ stencilFunc: THREE.NotEqualStencilFunc,
68
+ stencilFail: THREE.ReplaceStencilOp,
69
+ stencilZFail: THREE.ReplaceStencilOp,
70
+ stencilZPass: THREE.ReplaceStencilOp,
71
+ });
72
+
73
+ class CenteredPlane extends THREE.Plane {
74
+ constructor(normal, constant, center) {
75
+ super(normal, constant);
76
+ this.center = center;
77
+ this.setConstant(constant);
78
+ }
23
79
 
80
+ setConstant(value) {
81
+ this.centeredConstant = value;
82
+ const c = this.distanceToPoint(new THREE.Vector3(...this.center));
83
+ const z = this.distanceToPoint(new THREE.Vector3(0, 0, 0));
84
+ this.constant = z - c + value;
85
+ }
86
+ }
87
+
88
+ class PlaneMesh extends THREE.Mesh {
89
+ static matrix = new THREE.Matrix4();
90
+
91
+ constructor(index, plane, center, size, material, color, type) {
92
+ const meshGeometry = new THREE.PlaneGeometry(2, 2);
93
+ meshGeometry.computeBoundingSphere();
94
+ material.color.set(new THREE.Color(color));
95
+ super(meshGeometry, material);
96
+
97
+ this.type = type;
98
+ this.index = index;
24
99
  this.plane = plane;
25
100
  this.size = size;
26
101
  this.center = center;
27
-
28
- const positions2 = [
29
- 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1,
30
- ];
31
- const geometry2 = new THREE.BufferGeometry();
32
- geometry2.setAttribute(
33
- "position",
34
- new THREE.Float32BufferAttribute(positions2, 3),
35
- );
36
- geometry2.computeBoundingSphere();
37
-
38
- this.planeMesh = new THREE.Mesh(
39
- geometry2,
40
- new THREE.MeshBasicMaterial({
41
- color: color,
42
- opacity: 0.05,
43
- transparent: true,
44
- depthWrite: false,
45
- toneMapped: false,
46
- }),
47
- );
48
- this.add(this.planeMesh);
49
102
  }
50
103
 
51
- updateMatrixWorld(force) {
52
- let scale = -this.plane.constant;
104
+ dispose = () => {
105
+ this.geometry.dispose();
106
+ this.material.dispose();
107
+ for (var i = 0; i < this.children.length; i++) {
108
+ this.children[i].geometry.dispose();
109
+ this.children[i].material.dispose();
110
+ }
111
+ };
53
112
 
54
- if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter
113
+ onAfterRender = (renderer) => {
114
+ if (this.type.startsWith("StencilPlane")) {
115
+ renderer.clearStencil();
116
+ }
117
+ };
55
118
 
56
- this.scale.set(0.5 * this.size, 0.5 * this.size, scale);
119
+ // https://discourse.threejs.org/t/center-threejs-planehelper-on-geometry/48516/4
120
+ updateMatrixWorld(force) {
121
+ this.position.set(0, 0, 0);
122
+ this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
57
123
 
58
- // this.children[0].material.side = (scale < 0) ? THREE.BackSide : THREE.FrontSide; // renderer flips side when determinant < 0; flipping not wanted here
59
- this.children[0].material.side = THREE.DoubleSide;
60
- this.lookAt(this.plane.normal);
124
+ PlaneMesh.matrix.lookAt(this.position, this.plane.normal, this.up);
125
+ this.quaternion.setFromRotationMatrix(PlaneMesh.matrix);
61
126
 
62
- super.updateMatrixWorld(force);
127
+ this.translateZ(this.plane.constant);
128
+ super.updateMatrixWorld(this, force);
63
129
  }
64
130
  }
65
131
 
66
- class Clipping {
67
- constructor(center, size, distance, uiCallback, theme) {
68
- this.distance = distance;
69
- this.uiCallback = uiCallback;
132
+ function createStencil(name, material, geometry, plane) {
133
+ material.clippingPlanes = [plane];
134
+ var mesh = new THREE.Mesh(geometry, material);
135
+ mesh.name = name;
136
+ return mesh;
137
+ }
70
138
 
71
- const normals = [
72
- new THREE.Vector3(-1, 0, 0),
73
- new THREE.Vector3(0, -1, 0),
74
- new THREE.Vector3(0, 0, -1),
75
- ];
139
+ class Clipping {
140
+ constructor(center, size, nestedGroup, display, theme) {
141
+ this.center = center;
142
+ this.distance = size / 2;
143
+ this.display = display;
144
+ this.theme = theme;
145
+ this.nestedGroup = nestedGroup;
76
146
 
77
147
  this.clipPlanes = [];
78
-
79
- for (var i = 0; i < 3; i++) {
80
- this.clipPlanes.push(new THREE.Plane(normals[i], distance));
81
- this.uiCallback(i, normals[i].toArray());
82
- }
148
+ this.reverseClipPlanes = [];
83
149
 
84
150
  this.planeHelpers = new THREE.Group();
85
- this.planeHelpers.add(
86
- new PlaneHelper(
87
- 0,
88
- this.clipPlanes[0],
89
- center,
90
- size,
91
- theme === "light" ? 0xff0000 : 0xff4500,
92
- ),
93
- );
94
- this.planeHelpers.add(
95
- new PlaneHelper(
96
- 1,
97
- this.clipPlanes[1],
98
- center,
99
- size,
100
- theme === "light" ? 0x00ff00 : 0x32cd32,
101
- ),
102
- );
103
- this.planeHelpers.add(
104
- new PlaneHelper(
105
- 2,
106
- this.clipPlanes[2],
151
+ this.planeHelpers.name = "PlaneHelpers";
152
+ this.planeHelperMaterials = [];
153
+ this.objectColors = [];
154
+ this.objectColorCaps = false;
155
+
156
+ var i;
157
+ for (i = 0; i < 3; i++) {
158
+ const plane = new CenteredPlane(normals[i], this.distance, center);
159
+
160
+ this.clipPlanes.push(plane);
161
+ const reversePlane = new CenteredPlane(
162
+ normals[i].clone().negate(),
163
+ -this.distance,
107
164
  center,
108
- size,
109
- theme === "light" ? 0x0000ff : 0x3b9eff,
110
- ),
111
- );
165
+ );
166
+ this.reverseClipPlanes.push(reversePlane);
167
+
168
+ this.display.setNormalLabel(i, normals[i].toArray());
169
+
170
+ const material = planeHelperMaterial.clone();
171
+ material.opacity = (theme === "dark") ? 0.2 : 0.1;
172
+
173
+ this.planeHelperMaterials.push(material);
174
+ this.planeHelpers.add(
175
+ new PlaneMesh(
176
+ i,
177
+ plane,
178
+ center,
179
+ size,
180
+ material,
181
+ planeColors[theme][i],
182
+ "PlaneHelper",
183
+ ),
184
+ );
185
+ }
112
186
  this.planeHelpers.visible = false;
187
+
188
+ // Add clipping planes to the help planes
189
+ for (i = 0; i < 3; i++) {
190
+ const otherPlanes = this.clipPlanes.filter((_, j) => j !== i);
191
+ this.planeHelpers.children[i].material.clippingPlanes = otherPlanes;
192
+ }
193
+
194
+ /*
195
+ Stencils
196
+ */
197
+ var planeMeshGroup = new THREE.Group();
198
+ planeMeshGroup.name = "PlaneMeshes";
199
+
200
+ for (i = 0; i < 3; i++) {
201
+ const plane = this.clipPlanes[i];
202
+ const otherPlanes = this.clipPlanes.filter((_, j) => j !== i);
203
+ var j = 0;
204
+ for (var path in nestedGroup.groups) {
205
+ var clippingGroup = new THREE.Group();
206
+ clippingGroup.name = `clipping-${i}`;
207
+
208
+ var group = nestedGroup.groups[path];
209
+ if (group instanceof ObjectGroup && group.subtype === "solid") {
210
+ this.objectColors.push(group.children[0].material.color.getHex());
211
+
212
+ clippingGroup.add(
213
+ createStencil(
214
+ `frontStencil-${i}-${j}`,
215
+ frontStencilMaterial.clone(),
216
+ group.shapeGeometry,
217
+ plane,
218
+ ),
219
+ );
220
+
221
+ clippingGroup.add(
222
+ createStencil(
223
+ `backStencil-${i}-${j}`,
224
+ backStencilMaterial.clone(),
225
+ group.shapeGeometry,
226
+ plane,
227
+ ),
228
+ );
229
+
230
+ group.addType(clippingGroup, `clipping-${i}`);
231
+
232
+ var planeMaterial = stencilPlaneMaterial.clone();
233
+ planeMaterial.color.set(new THREE.Color(planeColors[theme][i]));
234
+ planeMaterial.clippingPlanes = otherPlanes;
235
+
236
+ planeMeshGroup.add(
237
+ new PlaneMesh(
238
+ i,
239
+ plane,
240
+ center,
241
+ size,
242
+ planeMaterial,
243
+ planeColors[theme][i],
244
+ `StencilPlane-${i}-${j}`,
245
+ )
246
+ );
247
+ j++;
248
+ }
249
+ }
250
+ }
251
+ nestedGroup.rootGroup.add(planeMeshGroup);
113
252
  }
114
253
 
254
+
115
255
  setConstant(index, value) {
116
- this.clipPlanes[index].constant = value;
256
+ this.clipPlanes[index].setConstant(value);
257
+ this.reverseClipPlanes[index].setConstant(-value);
117
258
  }
118
259
 
119
260
  setNormal = (index, normal) => {
120
- this.clipPlanes[index].normal = normal;
121
- this.uiCallback(index, normal.toArray());
261
+ var n = normal.clone();
262
+ this.clipPlanes[index].normal = n;
263
+ this.reverseClipPlanes[index].normal = n.clone().negate();
264
+ this.setConstant(index, this.distance);
265
+ this.display.setNormalLabel(index, n.toArray());
266
+ };
267
+
268
+ getObjectColorCaps = () => {
269
+ return this.objectColorCaps;
270
+ };
271
+
272
+ setObjectColorCaps = (flag) => {
273
+
274
+ var pmGroup;
275
+ for (pmGroup of this.nestedGroup.rootGroup.children) {
276
+ if (pmGroup.name === "PlaneMeshes") {
277
+ break;
278
+ }
279
+ }
280
+ var i = 0, j = -1;
281
+ const len = Object.keys(pmGroup.children).length / 3;
282
+ for (var group of pmGroup.children) {
283
+ if (i % len === 0) {
284
+ j++;
285
+ }
286
+ if (flag) {
287
+ group.material.color.set(new THREE.Color(this.objectColors[i]));
288
+ } else {
289
+ group.material.color.set(new THREE.Color(planeColors[this.theme][j]));
290
+ }
291
+ i++;
292
+ };
293
+ this.objectColorCaps = flag;
294
+ };
295
+
296
+ setVisible = (flag) => {
297
+
298
+ var pmGroup;
299
+ for (pmGroup of this.nestedGroup.rootGroup.children) {
300
+ if (pmGroup.name === "PlaneMeshes") {
301
+ break;
302
+ }
303
+ }
304
+ for (var group of pmGroup.children) {
305
+ group.material.visible = flag;
306
+ };
122
307
  };
123
308
  }
124
309