cyclecad 0.1.4 → 0.1.7

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.
@@ -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
  }
@@ -516,6 +540,62 @@ export function toggleWireframe(enabled) {
516
540
  }
517
541
  }
518
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
+
519
599
  // ============================================================================
520
600
  // Accessors
521
601
  // ============================================================================
@@ -664,4 +744,7 @@ export function dispose() {
664
744
  gridHelper = null;
665
745
  axisLines = null;
666
746
  referencePlanes = {};
747
+ groundPlane = null;
748
+ selectionOutline = null;
749
+ hemiLight = null;
667
750
  }