cyclecad 1.3.2 → 2.0.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/DRAWING_MODULE_INTEGRATION.md +633 -0
- package/README.md +124 -296
- package/app/index.html +2 -0
- package/app/js/brep-kernel.js +853 -0
- package/app/js/kernel.js +684 -0
- package/app/js/modules/assembly-module.js +582 -0
- package/app/js/modules/brep-module.js +583 -0
- package/app/js/modules/drawing-module.js +883 -0
- package/app/js/modules/operations-module.js +660 -0
- package/app/js/modules/simulation-module.js +834 -0
- package/app/js/modules/sketch-module.js +720 -0
- package/app/js/modules/step-module.js +510 -0
- package/app/js/modules/viewport-module.js +530 -0
- package/fusion360-gap-analysis.html +636 -0
- package/package.json +1 -1
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ViewportModule — Core 3D Rendering Engine
|
|
3
|
+
* Foundational LEGO block for Three.js scene, camera, renderer, and interaction
|
|
4
|
+
*
|
|
5
|
+
* Provides:
|
|
6
|
+
* - Scene setup (lights, grid, fog)
|
|
7
|
+
* - Camera (perspective + orbit controls)
|
|
8
|
+
* - Renderer (WebGL, shadows, MSAA)
|
|
9
|
+
* - Selection (raycaster + click detection)
|
|
10
|
+
* - View controls (preset views, fit-to-bounds)
|
|
11
|
+
*
|
|
12
|
+
* No dependencies — this is the base layer.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const ViewportModule = {
|
|
16
|
+
id: 'viewport',
|
|
17
|
+
name: '3D Viewport',
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
category: 'engine',
|
|
20
|
+
dependencies: [],
|
|
21
|
+
memoryEstimate: 30,
|
|
22
|
+
replaces: [],
|
|
23
|
+
|
|
24
|
+
async load(kernel) {
|
|
25
|
+
console.log('[ViewportModule] Loading...');
|
|
26
|
+
|
|
27
|
+
// Import THREE from global (via import map)
|
|
28
|
+
const THREE = window.THREE;
|
|
29
|
+
if (!THREE) throw new Error('THREE.js not loaded');
|
|
30
|
+
|
|
31
|
+
// Import OrbitControls
|
|
32
|
+
const { OrbitControls } = await import('https://cdn.jsdelivr.net/npm/three@r170/examples/jsm/controls/OrbitControls.js');
|
|
33
|
+
|
|
34
|
+
// === SCENE SETUP ===
|
|
35
|
+
const scene = new THREE.Scene();
|
|
36
|
+
scene.background = new THREE.Color(0x1a1a2e);
|
|
37
|
+
scene.fog = new THREE.Fog(0x1a1a2e, 100, 10000);
|
|
38
|
+
|
|
39
|
+
// === CAMERA ===
|
|
40
|
+
const camera = new THREE.PerspectiveCamera(
|
|
41
|
+
60,
|
|
42
|
+
window.innerWidth / window.innerHeight,
|
|
43
|
+
0.1,
|
|
44
|
+
100000
|
|
45
|
+
);
|
|
46
|
+
camera.position.set(500, 400, 500);
|
|
47
|
+
camera.lookAt(0, 0, 0);
|
|
48
|
+
|
|
49
|
+
// === RENDERER ===
|
|
50
|
+
const renderer = new THREE.WebGLRenderer({
|
|
51
|
+
antialias: true,
|
|
52
|
+
alpha: true,
|
|
53
|
+
preserveDrawingBuffer: true,
|
|
54
|
+
powerPreference: 'high-performance'
|
|
55
|
+
});
|
|
56
|
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
57
|
+
renderer.setPixelRatio(window.devicePixelRatio);
|
|
58
|
+
renderer.shadowMap.enabled = true;
|
|
59
|
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
60
|
+
renderer.localClippingEnabled = true; // For section cut
|
|
61
|
+
|
|
62
|
+
// === LIGHTS ===
|
|
63
|
+
// Ambient light for base illumination
|
|
64
|
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
|
65
|
+
scene.add(ambientLight);
|
|
66
|
+
|
|
67
|
+
// Directional lights for shadows
|
|
68
|
+
const dirLight1 = new THREE.DirectionalLight(0xffffff, 0.8);
|
|
69
|
+
dirLight1.position.set(500, 500, 500);
|
|
70
|
+
dirLight1.castShadow = true;
|
|
71
|
+
dirLight1.shadow.mapSize.width = 2048;
|
|
72
|
+
dirLight1.shadow.mapSize.height = 2048;
|
|
73
|
+
dirLight1.shadow.camera.left = -1000;
|
|
74
|
+
dirLight1.shadow.camera.right = 1000;
|
|
75
|
+
dirLight1.shadow.camera.top = 1000;
|
|
76
|
+
dirLight1.shadow.camera.bottom = -1000;
|
|
77
|
+
dirLight1.shadow.camera.far = 3000;
|
|
78
|
+
scene.add(dirLight1);
|
|
79
|
+
|
|
80
|
+
const dirLight2 = new THREE.DirectionalLight(0xffffff, 0.4);
|
|
81
|
+
dirLight2.position.set(-500, 300, -500);
|
|
82
|
+
dirLight2.castShadow = true;
|
|
83
|
+
scene.add(dirLight2);
|
|
84
|
+
|
|
85
|
+
const dirLight3 = new THREE.DirectionalLight(0xffffff, 0.3);
|
|
86
|
+
dirLight3.position.set(0, -500, 0);
|
|
87
|
+
scene.add(dirLight3);
|
|
88
|
+
|
|
89
|
+
// === GRID ===
|
|
90
|
+
const gridSize = 2000;
|
|
91
|
+
const gridDivisions = 20;
|
|
92
|
+
const grid = new THREE.GridHelper(gridSize, gridDivisions, 0x4a4a6a, 0x2a2a4a);
|
|
93
|
+
grid.position.y = 0;
|
|
94
|
+
grid.visible = true;
|
|
95
|
+
scene.add(grid);
|
|
96
|
+
|
|
97
|
+
// === CONTROLS ===
|
|
98
|
+
const controls = new OrbitControls(camera, renderer.domElement);
|
|
99
|
+
controls.enableDamping = true;
|
|
100
|
+
controls.dampingFactor = 0.05;
|
|
101
|
+
controls.autoRotate = false;
|
|
102
|
+
controls.mouseButtons = {
|
|
103
|
+
LEFT: THREE.MOUSE.ROTATE,
|
|
104
|
+
MIDDLE: THREE.MOUSE.DOLLY,
|
|
105
|
+
RIGHT: THREE.MOUSE.PAN
|
|
106
|
+
};
|
|
107
|
+
controls.touches = {
|
|
108
|
+
ONE: THREE.TOUCH.ROTATE,
|
|
109
|
+
TWO: THREE.TOUCH.DOLLY_PAN
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// === RAYCASTER (Selection) ===
|
|
113
|
+
const raycaster = new THREE.Raycaster();
|
|
114
|
+
const mouse = new THREE.Vector2();
|
|
115
|
+
const selectedObjects = new Set();
|
|
116
|
+
|
|
117
|
+
// === STATE & TRACKING ===
|
|
118
|
+
const state = {
|
|
119
|
+
gridVisible: true,
|
|
120
|
+
wireframeMode: false,
|
|
121
|
+
shadowsEnabled: true,
|
|
122
|
+
animationId: null,
|
|
123
|
+
viewName: 'iso',
|
|
124
|
+
selectedMeshId: null,
|
|
125
|
+
meshes: new Map(), // id -> { mesh, metadata }
|
|
126
|
+
nextMeshId: 0
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// === ANIMATION LOOP ===
|
|
130
|
+
function animate() {
|
|
131
|
+
state.animationId = requestAnimationFrame(animate);
|
|
132
|
+
controls.update();
|
|
133
|
+
renderer.render(scene, camera);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// === RESIZE HANDLER ===
|
|
137
|
+
function onWindowResize() {
|
|
138
|
+
const width = window.innerWidth;
|
|
139
|
+
const height = window.innerHeight;
|
|
140
|
+
camera.aspect = width / height;
|
|
141
|
+
camera.updateProjectionMatrix();
|
|
142
|
+
renderer.setSize(width, height);
|
|
143
|
+
kernel.bus.emit('viewport:resize', { width, height });
|
|
144
|
+
}
|
|
145
|
+
window.addEventListener('resize', onWindowResize);
|
|
146
|
+
|
|
147
|
+
// === CLICK HANDLER (Selection) ===
|
|
148
|
+
function onMouseClick(event) {
|
|
149
|
+
// Skip clicks on UI elements
|
|
150
|
+
if (event.target !== renderer.domElement) return;
|
|
151
|
+
|
|
152
|
+
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
|
153
|
+
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
|
154
|
+
|
|
155
|
+
raycaster.setFromCamera(mouse, camera);
|
|
156
|
+
const intersects = raycaster.intersectObjects(scene.children, true);
|
|
157
|
+
|
|
158
|
+
if (intersects.length > 0) {
|
|
159
|
+
const intersection = intersects[0];
|
|
160
|
+
const meshId = intersection.object.userData?.meshId;
|
|
161
|
+
|
|
162
|
+
if (meshId) {
|
|
163
|
+
state.selectedMeshId = meshId;
|
|
164
|
+
|
|
165
|
+
// Highlight selected object
|
|
166
|
+
selectedObjects.forEach(obj => {
|
|
167
|
+
obj.material.emissive.setHex(0x000000);
|
|
168
|
+
});
|
|
169
|
+
selectedObjects.clear();
|
|
170
|
+
|
|
171
|
+
intersection.object.material.emissive.setHex(0x444444);
|
|
172
|
+
selectedObjects.add(intersection.object);
|
|
173
|
+
|
|
174
|
+
kernel.bus.emit('part:selected', {
|
|
175
|
+
meshId,
|
|
176
|
+
point: intersection.point,
|
|
177
|
+
face: intersection.face,
|
|
178
|
+
object: intersection.object
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
// Deselect
|
|
183
|
+
selectedObjects.forEach(obj => {
|
|
184
|
+
obj.material.emissive.setHex(0x000000);
|
|
185
|
+
});
|
|
186
|
+
selectedObjects.clear();
|
|
187
|
+
state.selectedMeshId = null;
|
|
188
|
+
kernel.bus.emit('part:deselected', {});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
renderer.domElement.addEventListener('click', onMouseClick);
|
|
192
|
+
|
|
193
|
+
// === PRESET VIEWS ===
|
|
194
|
+
const presetViews = {
|
|
195
|
+
front: { pos: [0, 0, 500], target: [0, 0, 0] },
|
|
196
|
+
back: { pos: [0, 0, -500], target: [0, 0, 0] },
|
|
197
|
+
left: { pos: [-500, 0, 0], target: [0, 0, 0] },
|
|
198
|
+
right: { pos: [500, 0, 0], target: [0, 0, 0] },
|
|
199
|
+
top: { pos: [0, 500, 0], target: [0, 0, 0] },
|
|
200
|
+
bottom: { pos: [0, -500, 0], target: [0, 0, 0] },
|
|
201
|
+
iso: { pos: [500, 400, 500], target: [0, 0, 0] }
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// === HELPER FUNCTIONS ===
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Calculate bounds and fit camera
|
|
208
|
+
*/
|
|
209
|
+
function calculateBounds(object = scene) {
|
|
210
|
+
const box = new THREE.Box3().setFromObject(object);
|
|
211
|
+
if (box.isEmpty()) {
|
|
212
|
+
return { center: new THREE.Vector3(), size: 100 };
|
|
213
|
+
}
|
|
214
|
+
const size = box.getSize(new THREE.Vector3());
|
|
215
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
216
|
+
return { box, center, size };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Fit camera to object with animation
|
|
221
|
+
*/
|
|
222
|
+
function fitToObject(meshOrId, animate = true) {
|
|
223
|
+
let targetObj = meshOrId;
|
|
224
|
+
|
|
225
|
+
if (typeof meshOrId === 'string') {
|
|
226
|
+
const entry = state.meshes.get(meshOrId);
|
|
227
|
+
if (!entry) return console.warn(`[Viewport] Mesh ${meshOrId} not found`);
|
|
228
|
+
targetObj = entry.mesh;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const { box, center, size } = calculateBounds(targetObj);
|
|
232
|
+
const maxDim = Math.max(size.x, size.y, size.z);
|
|
233
|
+
const fov = camera.fov * (Math.PI / 180);
|
|
234
|
+
const distance = maxDim / (2 * Math.tan(fov / 2)) * 1.5;
|
|
235
|
+
|
|
236
|
+
const direction = camera.position.clone().sub(center).normalize();
|
|
237
|
+
const newPos = center.clone().add(direction.multiplyScalar(distance));
|
|
238
|
+
|
|
239
|
+
if (animate) {
|
|
240
|
+
// Smooth animation to new position
|
|
241
|
+
const startPos = camera.position.clone();
|
|
242
|
+
const startTime = performance.now();
|
|
243
|
+
const duration = 800; // ms
|
|
244
|
+
|
|
245
|
+
function animateCamera(currentTime) {
|
|
246
|
+
const elapsed = currentTime - startTime;
|
|
247
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
248
|
+
|
|
249
|
+
camera.position.lerpVectors(startPos, newPos, progress);
|
|
250
|
+
controls.target.lerpVectors(controls.target, center, progress);
|
|
251
|
+
controls.update();
|
|
252
|
+
|
|
253
|
+
if (progress < 1) {
|
|
254
|
+
requestAnimationFrame(animateCamera);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
requestAnimationFrame(animateCamera);
|
|
258
|
+
} else {
|
|
259
|
+
camera.position.copy(newPos);
|
|
260
|
+
controls.target.copy(center);
|
|
261
|
+
controls.update();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Set to preset view
|
|
267
|
+
*/
|
|
268
|
+
function setView(viewName) {
|
|
269
|
+
const view = presetViews[viewName];
|
|
270
|
+
if (!view) return console.warn(`[Viewport] Unknown view: ${viewName}`);
|
|
271
|
+
|
|
272
|
+
state.viewName = viewName;
|
|
273
|
+
camera.position.set(...view.pos);
|
|
274
|
+
controls.target.set(...view.target);
|
|
275
|
+
controls.update();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Toggle grid visibility
|
|
280
|
+
*/
|
|
281
|
+
function toggleGrid() {
|
|
282
|
+
state.gridVisible = !state.gridVisible;
|
|
283
|
+
grid.visible = state.gridVisible;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Toggle wireframe mode
|
|
288
|
+
*/
|
|
289
|
+
function toggleWireframe() {
|
|
290
|
+
state.wireframeMode = !state.wireframeMode;
|
|
291
|
+
scene.traverse(obj => {
|
|
292
|
+
if (obj.isMesh && obj.material) {
|
|
293
|
+
if (Array.isArray(obj.material)) {
|
|
294
|
+
obj.material.forEach(m => m.wireframe = state.wireframeMode);
|
|
295
|
+
} else {
|
|
296
|
+
obj.material.wireframe = state.wireframeMode;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Toggle shadows
|
|
304
|
+
*/
|
|
305
|
+
function toggleShadows() {
|
|
306
|
+
state.shadowsEnabled = !state.shadowsEnabled;
|
|
307
|
+
renderer.shadowMap.enabled = state.shadowsEnabled;
|
|
308
|
+
scene.traverse(obj => {
|
|
309
|
+
if (obj.isMesh) {
|
|
310
|
+
obj.castShadow = state.shadowsEnabled;
|
|
311
|
+
obj.receiveShadow = state.shadowsEnabled;
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Add mesh to scene
|
|
318
|
+
*/
|
|
319
|
+
function addMesh(geometry, material, name = 'Mesh') {
|
|
320
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
321
|
+
mesh.castShadow = state.shadowsEnabled;
|
|
322
|
+
mesh.receiveShadow = state.shadowsEnabled;
|
|
323
|
+
|
|
324
|
+
const meshId = `mesh_${state.nextMeshId++}`;
|
|
325
|
+
mesh.userData.meshId = meshId;
|
|
326
|
+
|
|
327
|
+
scene.add(mesh);
|
|
328
|
+
state.meshes.set(meshId, { mesh, geometry, material, name });
|
|
329
|
+
|
|
330
|
+
return meshId;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Remove mesh from scene
|
|
335
|
+
*/
|
|
336
|
+
function removeMesh(meshId) {
|
|
337
|
+
const entry = state.meshes.get(meshId);
|
|
338
|
+
if (!entry) return;
|
|
339
|
+
|
|
340
|
+
scene.remove(entry.mesh);
|
|
341
|
+
entry.geometry.dispose();
|
|
342
|
+
entry.material.dispose?.();
|
|
343
|
+
state.meshes.delete(meshId);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Set background color
|
|
348
|
+
*/
|
|
349
|
+
function setBackground(color) {
|
|
350
|
+
scene.background = new THREE.Color(color);
|
|
351
|
+
scene.fog.color.set(color);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Capture screenshot
|
|
356
|
+
*/
|
|
357
|
+
function screenshot(width = 1920, height = 1080) {
|
|
358
|
+
const oldSize = renderer.getSize(new THREE.Vector2());
|
|
359
|
+
renderer.setSize(width, height);
|
|
360
|
+
renderer.render(scene, camera);
|
|
361
|
+
const dataUrl = renderer.domElement.toDataURL('image/png');
|
|
362
|
+
renderer.setSize(oldSize.width, oldSize.height);
|
|
363
|
+
renderer.render(scene, camera);
|
|
364
|
+
return dataUrl;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// === REGISTER WITH KERNEL ===
|
|
368
|
+
kernel.state.set('scene', scene);
|
|
369
|
+
kernel.state.set('camera', camera);
|
|
370
|
+
kernel.state.set('renderer', renderer);
|
|
371
|
+
kernel.state.set('controls', controls);
|
|
372
|
+
kernel.state.set('raycaster', raycaster);
|
|
373
|
+
|
|
374
|
+
// Store module methods for later access
|
|
375
|
+
kernel.state.set('viewport', {
|
|
376
|
+
scene, camera, renderer, controls,
|
|
377
|
+
fitToObject, setView, toggleGrid, toggleWireframe, toggleShadows,
|
|
378
|
+
addMesh, removeMesh, setBackground, screenshot,
|
|
379
|
+
calculateBounds, state
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
console.log('[ViewportModule] Loaded successfully');
|
|
383
|
+
return { scene, camera, renderer, controls };
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
async activate(kernel) {
|
|
387
|
+
console.log('[ViewportModule] Activating...');
|
|
388
|
+
|
|
389
|
+
const container = document.getElementById('viewport-container') || (() => {
|
|
390
|
+
const div = document.createElement('div');
|
|
391
|
+
div.id = 'viewport-container';
|
|
392
|
+
div.style.cssText = 'position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden;';
|
|
393
|
+
document.body.appendChild(div);
|
|
394
|
+
return div;
|
|
395
|
+
})();
|
|
396
|
+
|
|
397
|
+
const viewport = kernel.state.get('viewport');
|
|
398
|
+
const renderer = kernel.state.get('renderer');
|
|
399
|
+
|
|
400
|
+
container.appendChild(renderer.domElement);
|
|
401
|
+
|
|
402
|
+
// Start animation loop
|
|
403
|
+
const { state } = viewport;
|
|
404
|
+
function animate() {
|
|
405
|
+
state.animationId = requestAnimationFrame(animate);
|
|
406
|
+
const controls = kernel.state.get('controls');
|
|
407
|
+
controls.update();
|
|
408
|
+
renderer.render(kernel.state.get('scene'), kernel.state.get('camera'));
|
|
409
|
+
}
|
|
410
|
+
animate();
|
|
411
|
+
|
|
412
|
+
kernel.bus.emit('viewport:ready', {});
|
|
413
|
+
console.log('[ViewportModule] Activated');
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
async deactivate(kernel) {
|
|
417
|
+
console.log('[ViewportModule] Deactivating...');
|
|
418
|
+
|
|
419
|
+
const viewport = kernel.state.get('viewport');
|
|
420
|
+
const renderer = kernel.state.get('renderer');
|
|
421
|
+
|
|
422
|
+
if (viewport.state.animationId) {
|
|
423
|
+
cancelAnimationFrame(viewport.state.animationId);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const container = document.getElementById('viewport-container');
|
|
427
|
+
if (container && renderer.domElement.parentNode === container) {
|
|
428
|
+
container.removeChild(renderer.domElement);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
console.log('[ViewportModule] Deactivated');
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
async unload(kernel) {
|
|
435
|
+
console.log('[ViewportModule] Unloading...');
|
|
436
|
+
|
|
437
|
+
const scene = kernel.state.get('scene');
|
|
438
|
+
const renderer = kernel.state.get('renderer');
|
|
439
|
+
const viewport = kernel.state.get('viewport');
|
|
440
|
+
|
|
441
|
+
// Dispose all geometries and materials
|
|
442
|
+
scene.traverse(obj => {
|
|
443
|
+
if (obj.geometry) obj.geometry.dispose();
|
|
444
|
+
if (obj.material) {
|
|
445
|
+
if (Array.isArray(obj.material)) {
|
|
446
|
+
obj.material.forEach(m => m.dispose());
|
|
447
|
+
} else {
|
|
448
|
+
obj.material.dispose();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Dispose renderer
|
|
454
|
+
renderer.dispose();
|
|
455
|
+
renderer.forceContextLoss();
|
|
456
|
+
|
|
457
|
+
// Clear state
|
|
458
|
+
kernel.state.delete('scene');
|
|
459
|
+
kernel.state.delete('camera');
|
|
460
|
+
kernel.state.delete('renderer');
|
|
461
|
+
kernel.state.delete('controls');
|
|
462
|
+
kernel.state.delete('raycaster');
|
|
463
|
+
kernel.state.delete('viewport');
|
|
464
|
+
|
|
465
|
+
console.log('[ViewportModule] Unloaded');
|
|
466
|
+
},
|
|
467
|
+
|
|
468
|
+
provides: {
|
|
469
|
+
commands: {
|
|
470
|
+
'viewport.fitAll': (kernel) => () => {
|
|
471
|
+
const viewport = kernel.state.get('viewport');
|
|
472
|
+
viewport.fitToObject(kernel.state.get('scene'));
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
'viewport.fitTo': (kernel) => (meshId) => {
|
|
476
|
+
const viewport = kernel.state.get('viewport');
|
|
477
|
+
viewport.fitToObject(meshId);
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
'viewport.setView': (kernel) => (viewName) => {
|
|
481
|
+
const viewport = kernel.state.get('viewport');
|
|
482
|
+
viewport.setView(viewName);
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
'viewport.toggleGrid': (kernel) => () => {
|
|
486
|
+
const viewport = kernel.state.get('viewport');
|
|
487
|
+
viewport.toggleGrid();
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
'viewport.toggleWireframe': (kernel) => () => {
|
|
491
|
+
const viewport = kernel.state.get('viewport');
|
|
492
|
+
viewport.toggleWireframe();
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
'viewport.toggleShadows': (kernel) => () => {
|
|
496
|
+
const viewport = kernel.state.get('viewport');
|
|
497
|
+
viewport.toggleShadows();
|
|
498
|
+
},
|
|
499
|
+
|
|
500
|
+
'viewport.addMesh': (kernel) => (geometry, material, name) => {
|
|
501
|
+
const viewport = kernel.state.get('viewport');
|
|
502
|
+
return viewport.addMesh(geometry, material, name);
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
'viewport.removeMesh': (kernel) => (meshId) => {
|
|
506
|
+
const viewport = kernel.state.get('viewport');
|
|
507
|
+
viewport.removeMesh(meshId);
|
|
508
|
+
},
|
|
509
|
+
|
|
510
|
+
'viewport.setBackground': (kernel) => (color) => {
|
|
511
|
+
const viewport = kernel.state.get('viewport');
|
|
512
|
+
viewport.setBackground(color);
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
'viewport.screenshot': (kernel) => (width, height) => {
|
|
516
|
+
const viewport = kernel.state.get('viewport');
|
|
517
|
+
return viewport.screenshot(width, height);
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
events: [
|
|
522
|
+
'part:selected',
|
|
523
|
+
'part:deselected',
|
|
524
|
+
'viewport:ready',
|
|
525
|
+
'viewport:resize'
|
|
526
|
+
]
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
export default ViewportModule;
|