cyclecad 0.1.3 → 0.1.5
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/CLAUDE.md +243 -0
- package/MASTERPLAN.md +182 -0
- package/app/index.html +1622 -30
- package/app/js/advanced-ops.js +762 -0
- package/app/js/app.js +79 -9
- package/app/js/assembly-resolver.js +477 -0
- package/app/js/assembly.js +1102 -0
- package/app/js/constraint-solver.js +1046 -0
- package/app/js/dxf-export.js +1173 -0
- package/app/js/operations.js +501 -112
- package/app/js/project-browser.js +741 -0
- package/app/js/project-loader.js +579 -0
- package/app/js/rebuild-guide.js +743 -0
- package/app/js/viewport.js +107 -0
- package/package.json +2 -2
package/app/js/viewport.js
CHANGED
|
@@ -29,6 +29,9 @@ let isAnimating = false;
|
|
|
29
29
|
let gridHelper = null;
|
|
30
30
|
let axisLines = null;
|
|
31
31
|
let referencePlanes = {};
|
|
32
|
+
let groundPlane = null;
|
|
33
|
+
let selectionOutline = null;
|
|
34
|
+
let hemiLight = null;
|
|
32
35
|
|
|
33
36
|
// Camera animation state
|
|
34
37
|
let cameraAnimationState = {
|
|
@@ -181,6 +184,10 @@ function setupLighting() {
|
|
|
181
184
|
const fillLight = new THREE.DirectionalLight(COLORS.fill, 0.3);
|
|
182
185
|
fillLight.position.set(-100, 50, -100);
|
|
183
186
|
scene.add(fillLight);
|
|
187
|
+
|
|
188
|
+
// Hemisphere light for natural environment feel (sky blue → ground grey)
|
|
189
|
+
hemiLight = new THREE.HemisphereLight(0x4488cc, 0x222222, 0.25);
|
|
190
|
+
scene.add(hemiLight);
|
|
184
191
|
}
|
|
185
192
|
|
|
186
193
|
// ============================================================================
|
|
@@ -238,6 +245,16 @@ function setupGridAndOrigin() {
|
|
|
238
245
|
axisLines = new THREE.LineSegments(axisGeometry, axisMaterial);
|
|
239
246
|
scene.add(axisLines);
|
|
240
247
|
|
|
248
|
+
// Ground shadow plane (invisible but receives shadows)
|
|
249
|
+
const groundGeom = new THREE.PlaneGeometry(GRID_SIZE * 2, GRID_SIZE * 2);
|
|
250
|
+
const groundMat = new THREE.ShadowMaterial({ opacity: 0.15 });
|
|
251
|
+
groundPlane = new THREE.Mesh(groundGeom, groundMat);
|
|
252
|
+
groundPlane.rotation.x = -Math.PI / 2;
|
|
253
|
+
groundPlane.position.y = -0.01; // Just below grid to avoid z-fighting
|
|
254
|
+
groundPlane.receiveShadow = true;
|
|
255
|
+
groundPlane.userData.isGround = true;
|
|
256
|
+
scene.add(groundPlane);
|
|
257
|
+
|
|
241
258
|
// Create reference planes (semi-transparent quads)
|
|
242
259
|
createReferencePlanes();
|
|
243
260
|
}
|
|
@@ -426,6 +443,13 @@ function calculateOptimalDistance() {
|
|
|
426
443
|
*/
|
|
427
444
|
export function addToScene(object) {
|
|
428
445
|
if (scene) {
|
|
446
|
+
// Auto-enable shadows on meshes
|
|
447
|
+
object.traverse((child) => {
|
|
448
|
+
if (child.isMesh) {
|
|
449
|
+
child.castShadow = true;
|
|
450
|
+
child.receiveShadow = true;
|
|
451
|
+
}
|
|
452
|
+
});
|
|
429
453
|
scene.add(object);
|
|
430
454
|
}
|
|
431
455
|
}
|
|
@@ -492,6 +516,86 @@ export function toggleReferencePlanes(visible, plane = null) {
|
|
|
492
516
|
}
|
|
493
517
|
}
|
|
494
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Toggle wireframe mode on all scene meshes
|
|
521
|
+
* @param {boolean} enabled - Enable/disable wireframe mode
|
|
522
|
+
*/
|
|
523
|
+
export function toggleWireframe(enabled) {
|
|
524
|
+
if (scene) {
|
|
525
|
+
scene.traverse((obj) => {
|
|
526
|
+
// Skip reference planes and grid
|
|
527
|
+
if (obj.userData && obj.userData.refPlane) return;
|
|
528
|
+
if (obj === gridHelper || obj === axisLines) return;
|
|
529
|
+
|
|
530
|
+
if (obj.material) {
|
|
531
|
+
if (Array.isArray(obj.material)) {
|
|
532
|
+
obj.material.forEach((m) => {
|
|
533
|
+
m.wireframe = enabled !== false;
|
|
534
|
+
});
|
|
535
|
+
} else {
|
|
536
|
+
obj.material.wireframe = enabled !== false;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// ============================================================================
|
|
544
|
+
// Selection Highlighting
|
|
545
|
+
// ============================================================================
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Highlight a mesh with a colored outline/glow effect
|
|
549
|
+
* @param {THREE.Mesh} mesh - Mesh to highlight
|
|
550
|
+
* @param {number} color - Highlight color (default: blue)
|
|
551
|
+
*/
|
|
552
|
+
export function highlightMesh(mesh, color = 0x58a6ff) {
|
|
553
|
+
// Remove existing highlight
|
|
554
|
+
clearHighlight();
|
|
555
|
+
|
|
556
|
+
if (!mesh || !mesh.geometry) return;
|
|
557
|
+
|
|
558
|
+
// Create outline using scaled clone with BackSide rendering
|
|
559
|
+
const outlineMat = new THREE.MeshBasicMaterial({
|
|
560
|
+
color: color,
|
|
561
|
+
side: THREE.BackSide,
|
|
562
|
+
transparent: true,
|
|
563
|
+
opacity: 0.4,
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
selectionOutline = new THREE.Mesh(mesh.geometry.clone(), outlineMat);
|
|
567
|
+
selectionOutline.scale.copy(mesh.scale).multiplyScalar(1.03);
|
|
568
|
+
selectionOutline.position.copy(mesh.position);
|
|
569
|
+
selectionOutline.rotation.copy(mesh.rotation);
|
|
570
|
+
selectionOutline.userData.isHighlight = true;
|
|
571
|
+
scene.add(selectionOutline);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Clear the current selection highlight
|
|
576
|
+
*/
|
|
577
|
+
export function clearHighlight() {
|
|
578
|
+
if (selectionOutline) {
|
|
579
|
+
scene.remove(selectionOutline);
|
|
580
|
+
if (selectionOutline.geometry) selectionOutline.geometry.dispose();
|
|
581
|
+
if (selectionOutline.material) selectionOutline.material.dispose();
|
|
582
|
+
selectionOutline = null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Enable castShadow on a mesh (call after adding to scene)
|
|
588
|
+
* @param {THREE.Object3D} object - Object to enable shadows on
|
|
589
|
+
*/
|
|
590
|
+
export function enableShadows(object) {
|
|
591
|
+
object.traverse((child) => {
|
|
592
|
+
if (child.isMesh) {
|
|
593
|
+
child.castShadow = true;
|
|
594
|
+
child.receiveShadow = true;
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
495
599
|
// ============================================================================
|
|
496
600
|
// Accessors
|
|
497
601
|
// ============================================================================
|
|
@@ -640,4 +744,7 @@ export function dispose() {
|
|
|
640
744
|
gridHelper = null;
|
|
641
745
|
axisLines = null;
|
|
642
746
|
referencePlanes = {};
|
|
747
|
+
groundPlane = null;
|
|
748
|
+
selectionOutline = null;
|
|
749
|
+
hemiLight = null;
|
|
643
750
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyclecad",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Browser-based parametric 3D CAD modeler with AI-powered tools, native Inventor file parsing, and smart assembly management. No install required.",
|
|
5
5
|
"main": "index.html",
|
|
6
6
|
"scripts": {
|
|
@@ -30,4 +30,4 @@
|
|
|
30
30
|
"bugs": {
|
|
31
31
|
"url": "https://github.com/vvlars-cmd/cyclecad/issues"
|
|
32
32
|
}
|
|
33
|
-
}
|
|
33
|
+
}
|