cyclecad 1.0.4 → 1.0.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/app/index.html +27 -3
- package/app/js/operations.js +51 -30
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -1419,7 +1419,7 @@
|
|
|
1419
1419
|
<span class="splash-logo-cycle">cycle</span><span class="splash-logo-cad">CAD</span>
|
|
1420
1420
|
</div>
|
|
1421
1421
|
<p class="splash-subtitle">Parametric 3D CAD Modeler for the Mechanical Designer</p>
|
|
1422
|
-
<p style="display:inline-block; color:#0066cc; font-size:1rem; margin:12px 0 0 0; letter-spacing:2px; font-family:monospace; font-weight:700; background:rgba(0,102,204,0.08); border:1.5px solid rgba(0,102,204,0.25); border-radius:8px; padding:5px 20px;">v1.0.
|
|
1422
|
+
<p style="display:inline-block; color:#0066cc; font-size:1rem; margin:12px 0 0 0; letter-spacing:2px; font-family:monospace; font-weight:700; background:rgba(0,102,204,0.08); border:1.5px solid rgba(0,102,204,0.25); border-radius:8px; padding:5px 20px;">v1.0.5</p>
|
|
1423
1423
|
</div>
|
|
1424
1424
|
<div class="splash-options">
|
|
1425
1425
|
<button class="splash-button splash-button-primary" id="btn-empty-project" style="grid-column: 1 / -1;">
|
|
@@ -4029,7 +4029,31 @@
|
|
|
4029
4029
|
panel.style.cssText = 'position:fixed;top:60px;right:320px;width:420px;max-height:80vh;overflow-y:auto;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;z-index:500;display:flex;flex-direction:column;box-shadow:var(--shadow-lg);';
|
|
4030
4030
|
|
|
4031
4031
|
const header = document.createElement('div');
|
|
4032
|
-
header.style.cssText = 'display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-bottom:1px solid var(--border-color);background:var(--bg-tertiary);border-radius:8px 8px 0 0;cursor:move;';
|
|
4032
|
+
header.style.cssText = 'display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-bottom:1px solid var(--border-color);background:var(--bg-tertiary);border-radius:8px 8px 0 0;cursor:move;user-select:none;';
|
|
4033
|
+
|
|
4034
|
+
// Make panel draggable by header
|
|
4035
|
+
let isDragging = false;
|
|
4036
|
+
let dragOffsetX = 0;
|
|
4037
|
+
let dragOffsetY = 0;
|
|
4038
|
+
|
|
4039
|
+
header.addEventListener('mousedown', (e) => {
|
|
4040
|
+
isDragging = true;
|
|
4041
|
+
const rect = panel.getBoundingClientRect();
|
|
4042
|
+
dragOffsetX = e.clientX - rect.left;
|
|
4043
|
+
dragOffsetY = e.clientY - rect.top;
|
|
4044
|
+
panel.style.zIndex = '9999'; // Bring to front
|
|
4045
|
+
});
|
|
4046
|
+
|
|
4047
|
+
document.addEventListener('mousemove', (e) => {
|
|
4048
|
+
if (!isDragging || !panel.parentElement) return;
|
|
4049
|
+
panel.style.left = (e.clientX - dragOffsetX) + 'px';
|
|
4050
|
+
panel.style.top = (e.clientY - dragOffsetY) + 'px';
|
|
4051
|
+
panel.style.right = 'auto'; // Remove right constraint when dragging
|
|
4052
|
+
});
|
|
4053
|
+
|
|
4054
|
+
document.addEventListener('mouseup', () => {
|
|
4055
|
+
isDragging = false;
|
|
4056
|
+
});
|
|
4033
4057
|
|
|
4034
4058
|
const titles = {
|
|
4035
4059
|
'copilot-panel': '✨ AI Copilot',
|
|
@@ -5243,6 +5267,6 @@
|
|
|
5243
5267
|
</div>
|
|
5244
5268
|
</div>
|
|
5245
5269
|
|
|
5246
|
-
<span id="version-badge" style="position:fixed;bottom:42px;left:50%;transform:translateX(-50%);z-index:999;font-size:0.9rem;color:rgba(255,255,255,0.9);letter-spacing:0.1em;white-space:nowrap;padding:6px 16px;user-select:all;pointer-events:auto;font-family:monospace;font-weight:700;background:rgba(0,0,0,0.7);border:1px solid rgba(88,166,255,0.4);border-radius:6px;text-shadow:0 1px 3px rgba(0,0,0,0.5);" title="cycleCAD version">cycleCAD v1.0.
|
|
5270
|
+
<span id="version-badge" style="position:fixed;bottom:42px;left:50%;transform:translateX(-50%);z-index:999;font-size:0.9rem;color:rgba(255,255,255,0.9);letter-spacing:0.1em;white-space:nowrap;padding:6px 16px;user-select:all;pointer-events:auto;font-family:monospace;font-weight:700;background:rgba(0,0,0,0.7);border:1px solid rgba(88,166,255,0.4);border-radius:6px;text-shadow:0 1px 3px rgba(0,0,0,0.5);" title="cycleCAD version">cycleCAD v1.0.5</span>
|
|
5247
5271
|
</body>
|
|
5248
5272
|
</html>
|
package/app/js/operations.js
CHANGED
|
@@ -349,12 +349,17 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
349
349
|
const { material = 'steel' } = options;
|
|
350
350
|
let geometry;
|
|
351
351
|
|
|
352
|
+
// Scale factor: AI copilot provides dimensions in millimeters, but we render at 1:1 scene units
|
|
353
|
+
// For a 100mm cube to look reasonable in viewport, scale down by 1.0 (1:1 scale)
|
|
354
|
+
// Most CAD systems use 1 scene unit = 1 mm
|
|
355
|
+
const SCALE = 1.0;
|
|
356
|
+
|
|
352
357
|
switch (type) {
|
|
353
358
|
case 'box':
|
|
354
359
|
geometry = new THREE.BoxGeometry(
|
|
355
|
-
params.width ||
|
|
356
|
-
params.height ||
|
|
357
|
-
params.depth ||
|
|
360
|
+
(params.width || 10) * SCALE,
|
|
361
|
+
(params.height || 10) * SCALE,
|
|
362
|
+
(params.depth || 10) * SCALE,
|
|
358
363
|
params.widthSegments || 1,
|
|
359
364
|
params.heightSegments || 1,
|
|
360
365
|
params.depthSegments || 1
|
|
@@ -363,9 +368,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
363
368
|
|
|
364
369
|
case 'cylinder':
|
|
365
370
|
geometry = new THREE.CylinderGeometry(
|
|
366
|
-
params.radius ||
|
|
367
|
-
params.radius ||
|
|
368
|
-
params.height ||
|
|
371
|
+
(params.radius || 10) * SCALE,
|
|
372
|
+
(params.radius || 10) * SCALE,
|
|
373
|
+
(params.height || 20) * SCALE,
|
|
369
374
|
params.segments || 32,
|
|
370
375
|
1,
|
|
371
376
|
params.openEnded || false
|
|
@@ -374,7 +379,7 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
374
379
|
|
|
375
380
|
case 'sphere':
|
|
376
381
|
geometry = new THREE.SphereGeometry(
|
|
377
|
-
params.radius ||
|
|
382
|
+
(params.radius || 10) * SCALE,
|
|
378
383
|
params.segments || 32,
|
|
379
384
|
params.segments || 32
|
|
380
385
|
);
|
|
@@ -382,26 +387,42 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
382
387
|
|
|
383
388
|
case 'cone':
|
|
384
389
|
geometry = new THREE.ConeGeometry(
|
|
385
|
-
params.bottomRadius ||
|
|
386
|
-
params.height ||
|
|
390
|
+
(params.bottomRadius || 10) * SCALE,
|
|
391
|
+
(params.height || 20) * SCALE,
|
|
387
392
|
params.segments || 32
|
|
388
393
|
);
|
|
389
394
|
break;
|
|
390
395
|
|
|
391
396
|
case 'torus':
|
|
392
397
|
geometry = new THREE.TorusGeometry(
|
|
393
|
-
params.radius ||
|
|
394
|
-
params.tube ||
|
|
398
|
+
(params.radius || 10) * SCALE,
|
|
399
|
+
(params.tube || 4) * SCALE,
|
|
395
400
|
params.radialSegments || 16,
|
|
396
401
|
params.tubeSegments || 100
|
|
397
402
|
);
|
|
398
403
|
break;
|
|
399
404
|
|
|
405
|
+
case 'hole': {
|
|
406
|
+
// Create a cylinder for hole preview (visual aid — actual hole subtraction happens in boolean operations)
|
|
407
|
+
// Used by feature.hole commands from AI copilot
|
|
408
|
+
const holeRadius = (params.radius || 5) * SCALE;
|
|
409
|
+
const holeDepth = (params.depth || 20) * SCALE;
|
|
410
|
+
geometry = new THREE.CylinderGeometry(
|
|
411
|
+
holeRadius,
|
|
412
|
+
holeRadius,
|
|
413
|
+
holeDepth,
|
|
414
|
+
16,
|
|
415
|
+
1,
|
|
416
|
+
false
|
|
417
|
+
);
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
|
|
400
421
|
case 'bracket': {
|
|
401
422
|
// L-shaped bracket from width, height, thickness
|
|
402
|
-
const bw = params.width || 80;
|
|
403
|
-
const bh = params.height || 40;
|
|
404
|
-
const bt = params.thickness || 5;
|
|
423
|
+
const bw = (params.width || 80) * SCALE;
|
|
424
|
+
const bh = (params.height || 40) * SCALE;
|
|
425
|
+
const bt = (params.thickness || 5) * SCALE;
|
|
405
426
|
const shape = new THREE.Shape();
|
|
406
427
|
shape.moveTo(0, 0);
|
|
407
428
|
shape.lineTo(bw, 0);
|
|
@@ -417,9 +438,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
417
438
|
|
|
418
439
|
case 'flange': {
|
|
419
440
|
// Annular flange: outer ring with center hole
|
|
420
|
-
const fo = (params.outerDiameter || params.outerRadius * 2 || 60) / 2;
|
|
421
|
-
const fi = (params.innerDiameter || params.innerRadius * 2 || 20) / 2;
|
|
422
|
-
const fh = params.height || params.thickness || 10;
|
|
441
|
+
const fo = ((params.outerDiameter || params.outerRadius * 2 || 60) / 2) * SCALE;
|
|
442
|
+
const fi = ((params.innerDiameter || params.innerRadius * 2 || 20) / 2) * SCALE;
|
|
443
|
+
const fh = (params.height || params.thickness || 10) * SCALE;
|
|
423
444
|
const outerShape = new THREE.Shape();
|
|
424
445
|
outerShape.absarc(0, 0, fo, 0, Math.PI * 2, false);
|
|
425
446
|
const holePath = new THREE.Path();
|
|
@@ -431,9 +452,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
431
452
|
}
|
|
432
453
|
|
|
433
454
|
case 'washer': {
|
|
434
|
-
const wo = (params.outerDiameter || 20) / 2;
|
|
435
|
-
const wi = (params.innerDiameter || 10) / 2;
|
|
436
|
-
const wt = params.thickness || 2;
|
|
455
|
+
const wo = ((params.outerDiameter || 20) / 2) * SCALE;
|
|
456
|
+
const wi = ((params.innerDiameter || 10) / 2) * SCALE;
|
|
457
|
+
const wt = (params.thickness || 2) * SCALE;
|
|
437
458
|
const wShape = new THREE.Shape();
|
|
438
459
|
wShape.absarc(0, 0, wo, 0, Math.PI * 2, false);
|
|
439
460
|
const wHole = new THREE.Path();
|
|
@@ -445,9 +466,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
445
466
|
}
|
|
446
467
|
|
|
447
468
|
case 'spacer': {
|
|
448
|
-
const so = (params.outerDiameter || params.diameter || 15) / 2;
|
|
449
|
-
const si = (params.innerDiameter || params.holeDiameter || 6) / 2;
|
|
450
|
-
const sh = params.height || params.length || 20;
|
|
469
|
+
const so = (((params.outerDiameter || params.diameter || 15) / 2) * SCALE);
|
|
470
|
+
const si = (((params.innerDiameter || params.holeDiameter || 6) / 2) * SCALE);
|
|
471
|
+
const sh = (params.height || params.length || 20) * SCALE;
|
|
451
472
|
const sShape = new THREE.Shape();
|
|
452
473
|
sShape.absarc(0, 0, so, 0, Math.PI * 2, false);
|
|
453
474
|
const sHole = new THREE.Path();
|
|
@@ -460,9 +481,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
460
481
|
|
|
461
482
|
case 'gear': {
|
|
462
483
|
// Simple spur gear approximation
|
|
463
|
-
const gr = params.radius || params.diameter / 2 || 30;
|
|
484
|
+
const gr = ((params.radius || params.diameter / 2 || 30) * SCALE);
|
|
464
485
|
const gt = params.teeth || 20;
|
|
465
|
-
const gd = params.depth || params.thickness || 10;
|
|
486
|
+
const gd = (params.depth || params.thickness || 10) * SCALE;
|
|
466
487
|
const toothH = gr * 0.15;
|
|
467
488
|
const gShape = new THREE.Shape();
|
|
468
489
|
for (let i = 0; i < gt; i++) {
|
|
@@ -482,9 +503,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
482
503
|
}
|
|
483
504
|
|
|
484
505
|
case 'plate': {
|
|
485
|
-
const pw = params.width || 100;
|
|
486
|
-
const ph = params.height || params.depth || 50;
|
|
487
|
-
const pt = params.thickness || 5;
|
|
506
|
+
const pw = (params.width || 100) * SCALE;
|
|
507
|
+
const ph = (params.height || params.depth || 50) * SCALE;
|
|
508
|
+
const pt = (params.thickness || 5) * SCALE;
|
|
488
509
|
geometry = new THREE.BoxGeometry(pw, pt, ph);
|
|
489
510
|
break;
|
|
490
511
|
}
|
|
@@ -493,8 +514,8 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
493
514
|
// Fallback: treat unknown as a box with available dimensions
|
|
494
515
|
console.warn(`Unknown primitive type "${type}" — creating box fallback`);
|
|
495
516
|
geometry = new THREE.BoxGeometry(
|
|
496
|
-
params.width || params.diameter || 50,
|
|
497
|
-
params.height || params.thickness || 50,
|
|
517
|
+
(params.width || params.diameter || 50) * SCALE,
|
|
518
|
+
(params.height || params.thickness || 50) * SCALE,
|
|
498
519
|
params.depth || params.thickness || 50
|
|
499
520
|
);
|
|
500
521
|
}
|
package/package.json
CHANGED