forgecad 0.1.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/LICENSE +97 -0
- package/README.md +354 -0
- package/dist/assets/evalWorker-BYHXxh15.js +461 -0
- package/dist/assets/index--CYbOPKS.js +5797 -0
- package/dist/assets/manifold-65fIQlgQ.js +20 -0
- package/dist/assets/manifold-B85M7kop.js +20 -0
- package/dist/assets/manifold-B8h_vZ5O.js +16 -0
- package/dist/assets/manifold-D9yvTBHx.wasm +0 -0
- package/dist/assets/manifold-d1UpyLJ8.js +20 -0
- package/dist/assets/reportWorker-B1Zdrz9l.js +494 -0
- package/dist/index.html +16 -0
- package/dist-cli/forgecad.js +44464 -0
- package/dist-skill/SKILL.md +4635 -0
- package/examples/3d-printer.forge.js +328 -0
- package/examples/5-figen-robot-hand.forge.js +283 -0
- package/examples/ac-unit-glm47.forge.js +108 -0
- package/examples/ac-unit-glm5.forge.js +174 -0
- package/examples/ac-unit-kimi25.forge.js +236 -0
- package/examples/ac-unit-minimax.forge.js +123 -0
- package/examples/ac-unit.forge.js +126 -0
- package/examples/adjustable-table.forge.js +191 -0
- package/examples/api/assembly-gear-coupling.forge.js +32 -0
- package/examples/api/assembly-mechanism.forge.js +111 -0
- package/examples/api/attachTo-basics.forge.js +45 -0
- package/examples/api/benchy-style-hull.forge.js +89 -0
- package/examples/api/bill-of-materials.forge.js +46 -0
- package/examples/api/boolean-operations.forge.js +48 -0
- package/examples/api/bounding-box-visualizer.forge.js +58 -0
- package/examples/api/brep-exportable.forge.js +19 -0
- package/examples/api/center-true-vs-false.forge.js +40 -0
- package/examples/api/clone-duplicate.forge.js +41 -0
- package/examples/api/colors-union-vs-array.forge.js +27 -0
- package/examples/api/coordinate-system.forge.js +54 -0
- package/examples/api/curves-surfacing-basics.forge.js +91 -0
- package/examples/api/dimensioned-bracket.forge.js +19 -0
- package/examples/api/elbow-test.forge.js +23 -0
- package/examples/api/exploded-view.forge.js +60 -0
- package/examples/api/extrude-options.forge.js +44 -0
- package/examples/api/face-gears.forge.js +44 -0
- package/examples/api/face-transformation-history.forge.js +45 -0
- package/examples/api/feature-created-faces.forge.js +47 -0
- package/examples/api/folded-service-panel-cover.forge.js +3 -0
- package/examples/api/folded-service-panel-cover.js +117 -0
- package/examples/api/gears-bevel-face-joints.forge.js +157 -0
- package/examples/api/gears-tier1.forge.js +57 -0
- package/examples/api/geometry-info.forge.js +49 -0
- package/examples/api/group-test.forge.js +34 -0
- package/examples/api/group-vs-union.forge.js +25 -0
- package/examples/api/import-args-unit.forge.js +5 -0
- package/examples/api/import-args.forge.js +16 -0
- package/examples/api/import-dimensions-follow.forge.js +18 -0
- package/examples/api/import-placement-references.forge.js +18 -0
- package/examples/api/import-placement-widget-source.forge.js +30 -0
- package/examples/api/import-relative-paths.forge.js +18 -0
- package/examples/api/import-svg-sketch-shape.svg +15 -0
- package/examples/api/import-svg-sketch.forge.js +28 -0
- package/examples/api/js-module-imports.forge.js +9 -0
- package/examples/api/js-module-pillars.js +25 -0
- package/examples/api/js-module-scene.js +9 -0
- package/examples/api/notebook-assembly-debug.forge-notebook.json +90 -0
- package/examples/api/notebook-iteration.forge-notebook.json +75 -0
- package/examples/api/patterns.forge.js +32 -0
- package/examples/api/pointAlong-orientation.forge.js +52 -0
- package/examples/api/profile-2020-b-slot6.forge.js +36 -0
- package/examples/api/rotate-around-to.forge.js +31 -0
- package/examples/api/runtime-joints-view.forge.js +116 -0
- package/examples/api/sdf-rover-demo.forge.js +159 -0
- package/examples/api/section-plane-visualization.forge.js +38 -0
- package/examples/api/sketch-basics.forge.js +48 -0
- package/examples/api/sketch-on-face.forge.js +56 -0
- package/examples/api/sketch-rounding-strategies.forge.js +56 -0
- package/examples/api/spatial-recipes.forge.js +129 -0
- package/examples/bathroom.forge.js +197 -0
- package/examples/bolt-and-nut.forge.js +39 -0
- package/examples/bolt-pattern.forge.js +18 -0
- package/examples/bottle.forge.js +101 -0
- package/examples/chair.forge.js +62 -0
- package/examples/chess-set.forge.js +232 -0
- package/examples/classical-piano.forge.js +203 -0
- package/examples/clock.forge.js +169 -0
- package/examples/compiler-corpus/README.md +88 -0
- package/examples/compiler-corpus/edge-finished-mount.forge.js +18 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +24 -0
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +42 -0
- package/examples/compiler-corpus/folded-service-panel-cover.forge.js +5 -0
- package/examples/compiler-corpus/motor-mount-plate.forge.js +32 -0
- package/examples/compiler-corpus/projection-relay-cover.forge.js +16 -0
- package/examples/compiler-corpus/sensor-bracket.forge.js +35 -0
- package/examples/compiler-corpus/service-panel-cover.forge.js +53 -0
- package/examples/compiler-corpus/trimmed-access-cover.forge.js +26 -0
- package/examples/cup.forge.js +25 -0
- package/examples/cut-plane-demo.forge.js +28 -0
- package/examples/door-with-hinges.forge.js +54 -0
- package/examples/frame.sketch.js +4 -0
- package/examples/headphone-hanger-profile.sketch.js +18 -0
- package/examples/headphone-hanger-v2.forge.js +88 -0
- package/examples/headphone-hanger.forge.js +5 -0
- package/examples/iphone-stand.forge.js +72 -0
- package/examples/iphone.forge.js +114 -0
- package/examples/ironman-helmet.js +79 -0
- package/examples/kitchen.forge.js +231 -0
- package/examples/lamp-shade.sketch.js +17 -0
- package/examples/laptop.forge.js +144 -0
- package/examples/liquid-soap-dispenser.forge.js +159 -0
- package/examples/modern-tv.forge.js +86 -0
- package/examples/picture-frame.forge.js +34 -0
- package/examples/robot_hand.forge.js +393 -0
- package/examples/robot_hand_2.forge.js +622 -0
- package/examples/sandbox.forge.js +3 -0
- package/examples/shelf/container.forge.js +30 -0
- package/examples/shelf/shelf-unit.forge.js +62 -0
- package/examples/shoe-rack-doors.forge.js +107 -0
- package/examples/shoe-rack.forge.js +65 -0
- package/examples/spiderman-cake.forge.js +92 -0
- package/examples/table-lamp.forge.js +33 -0
- package/examples/table.forge.js +44 -0
- package/examples/test-colors.forge.js +19 -0
- package/examples/tv-stand.forge.js +21 -0
- package/package.json +69 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// AC Unit Model
|
|
2
|
+
// Shows inside and outside units separated by a wall
|
|
3
|
+
|
|
4
|
+
// === Parameters ===
|
|
5
|
+
const wallThick = param("Wall Thickness", 200, { min: 100, max: 400, unit: "mm" });
|
|
6
|
+
const wallHeight = param("Wall Height", 2500, { min: 1500, max: 4000, unit: "mm" });
|
|
7
|
+
const wallWidth = param("Wall Width", 3000, { min: 1000, max: 5000, unit: "mm" });
|
|
8
|
+
|
|
9
|
+
// Inside Unit (Evaporator)
|
|
10
|
+
const inW = param("Inside Width", 800, { min: 400, max: 1200, unit: "mm" });
|
|
11
|
+
const inH = param("Inside Height", 300, { min: 200, max: 500, unit: "mm" });
|
|
12
|
+
const inD = param("Inside Depth", 250, { min: 150, max: 400, unit: "mm" });
|
|
13
|
+
|
|
14
|
+
// Outside Unit (Condenser)
|
|
15
|
+
const outW = param("Outside Width", 900, { min: 500, max: 1500, unit: "mm" });
|
|
16
|
+
const outH = param("Outside Height", 700, { min: 400, max: 1000, unit: "mm" });
|
|
17
|
+
const outD = param("Outside Depth", 400, { min: 200, max: 600, unit: "mm" });
|
|
18
|
+
|
|
19
|
+
// === Wall ===
|
|
20
|
+
// Create a wall with a hole for the pipes
|
|
21
|
+
const wall = box(wallWidth, wallThick, wallHeight, true);
|
|
22
|
+
const pipeHole = cylinder(wallThick + 10, 60).rotate(90, 0, 0);
|
|
23
|
+
const wallWithHole = wall.subtract(pipeHole);
|
|
24
|
+
|
|
25
|
+
// === Inside Unit ===
|
|
26
|
+
// Main housing
|
|
27
|
+
const insideBox = box(inW, inD, inH, true);
|
|
28
|
+
|
|
29
|
+
// Air vents (slats on the front facing the room)
|
|
30
|
+
const vents = [];
|
|
31
|
+
const ventCount = 8;
|
|
32
|
+
const ventPadZ = 20; // Padding from top/bottom
|
|
33
|
+
const ventSpacing = (inH - 2 * ventPadZ) / (ventCount - 1);
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < ventCount; i++) {
|
|
36
|
+
// Centered box (true) ensures it aligns with the unit's center X
|
|
37
|
+
vents.push(
|
|
38
|
+
box(inW - 20, 2, 4, true)
|
|
39
|
+
.translate(0, -inD / 2 - 1, -inH / 2 + ventPadZ + i * ventSpacing)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Control panel area (simple box on top right)
|
|
44
|
+
// Centered box, translated so its right edge aligns with the unit's right edge
|
|
45
|
+
const panelW = 100;
|
|
46
|
+
const panel = box(panelW, 10, 40, true)
|
|
47
|
+
.translate(inW / 2 - panelW / 2, -inD / 2 - 5, inH / 2 - 20);
|
|
48
|
+
|
|
49
|
+
const insideUnit = union(insideBox, ...vents, panel)
|
|
50
|
+
.translate(0, -wallThick / 2 - inD / 2, 0);
|
|
51
|
+
|
|
52
|
+
// === Outside Unit ===
|
|
53
|
+
// Main housing
|
|
54
|
+
const outsideBox = box(outW, outD, outH, true);
|
|
55
|
+
|
|
56
|
+
// Fan grill (grid of holes on the back)
|
|
57
|
+
const grillHoles = [];
|
|
58
|
+
const grillRows = 5;
|
|
59
|
+
const grillCols = 8;
|
|
60
|
+
const grillPadX = 40;
|
|
61
|
+
const grillPadZ = 40;
|
|
62
|
+
const grillStepX = (outW - 2 * grillPadX) / (grillCols - 1);
|
|
63
|
+
const grillStepZ = (outH - 2 * grillPadZ) / (grillRows - 1);
|
|
64
|
+
|
|
65
|
+
for (let r = 0; r < grillRows; r++) {
|
|
66
|
+
for (let c = 0; c < grillCols; c++) {
|
|
67
|
+
grillHoles.push(
|
|
68
|
+
cylinder(10, 8).translate(
|
|
69
|
+
-outW / 2 + grillPadX + c * grillStepX,
|
|
70
|
+
outD / 2 + 1,
|
|
71
|
+
-outH / 2 + grillPadZ + r * grillStepZ
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Compressor bump (raised area on top)
|
|
78
|
+
// Centered box to align with unit
|
|
79
|
+
const compressorBump = box(outW * 0.6, outD * 0.6, 50, true)
|
|
80
|
+
.translate(0, 0, outH / 2 - 25);
|
|
81
|
+
|
|
82
|
+
const outsideUnit = outsideBox
|
|
83
|
+
.subtract(...grillHoles)
|
|
84
|
+
.add(compressorBump)
|
|
85
|
+
.translate(0, wallThick / 2 + outD / 2, 0);
|
|
86
|
+
|
|
87
|
+
// === Connecting Pipes ===
|
|
88
|
+
// Two pipes running through the wall connecting the units
|
|
89
|
+
// Length spans the wall thickness. Centered (true) so they bridge the gap exactly.
|
|
90
|
+
const pipeRadius = 15;
|
|
91
|
+
const pipeOffsetX = inW * 0.25; // Position pipes at 25% width
|
|
92
|
+
const pipeZ1 = -inH / 2 + inH * 0.2; // Lower pipe
|
|
93
|
+
const pipeZ2 = -inH / 2 + inH * 0.4; // Upper pipe
|
|
94
|
+
|
|
95
|
+
const pipe1 = cylinder(wallThick, pipeRadius, undefined, undefined, true)
|
|
96
|
+
.rotate(90, 0, 0)
|
|
97
|
+
.translate(-pipeOffsetX, 0, pipeZ1);
|
|
98
|
+
const pipe2 = cylinder(wallThick, pipeRadius, undefined, undefined, true)
|
|
99
|
+
.rotate(90, 0, 0)
|
|
100
|
+
.translate(pipeOffsetX, 0, pipeZ2);
|
|
101
|
+
|
|
102
|
+
// === Assembly ===
|
|
103
|
+
return [
|
|
104
|
+
{ name: "Wall", shape: wallWithHole.color("#CCCCCC") },
|
|
105
|
+
{ name: "Inside Unit", shape: insideUnit.color("#F0F0F0") },
|
|
106
|
+
{ name: "Outside Unit", shape: outsideUnit.color("#E0E0E0") },
|
|
107
|
+
{ name: "Pipes", shape: union(pipe1, pipe2).color("#333333") }
|
|
108
|
+
];
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Home AC Unit V3 - Fixed cylinder orientation and positioning
|
|
2
|
+
|
|
3
|
+
// Parameters
|
|
4
|
+
const wallThick = param("Wall Thickness", 20, { min: 10, max: 40, unit: "mm" });
|
|
5
|
+
const indoorWidth = param("Indoor Width", 80, { min: 60, max: 120, unit: "mm" });
|
|
6
|
+
const indoorHeight = param("Indoor Height", 30, { min: 20, max: 50, unit: "mm" });
|
|
7
|
+
const indoorDepth = param("Indoor Depth", 20, { min: 15, max: 35, unit: "mm" });
|
|
8
|
+
const outdoorWidth = param("Outdoor Width", 70, { min: 50, max: 100, unit: "mm" });
|
|
9
|
+
const outdoorHeight = param("Outdoor Height", 55, { min: 40, max: 80, unit: "mm" });
|
|
10
|
+
const outdoorDepth = param("Outdoor Depth", 30, { min: 20, max: 50, unit: "mm" });
|
|
11
|
+
|
|
12
|
+
// --- Wall (reference) ---
|
|
13
|
+
// Centered on Y=0: front at Y=-wallThick/2, back at Y=+wallThick/2
|
|
14
|
+
const wall = box(120, wallThick, 100, true).color('#d4c4a8');
|
|
15
|
+
|
|
16
|
+
// --- INDOOR UNIT (on FRONT of wall, -Y side) ---
|
|
17
|
+
const indoorBody = box(indoorWidth, indoorDepth, indoorHeight, true)
|
|
18
|
+
.color('#f5f5f5')
|
|
19
|
+
.attachTo(wall, 'front', 'back', [0, 0, 20]); // Raised 20mm above center
|
|
20
|
+
|
|
21
|
+
// Front panel (flush with indoor body front)
|
|
22
|
+
const frontPanel = roundedRect(indoorWidth - 4, indoorHeight - 4, 3, true)
|
|
23
|
+
.extrude(2)
|
|
24
|
+
.color('#ffffff')
|
|
25
|
+
.attachTo(indoorBody, 'front', 'back');
|
|
26
|
+
|
|
27
|
+
// Air outlet vents
|
|
28
|
+
const ventCount = 5;
|
|
29
|
+
const ventSpacing = indoorHeight / (ventCount + 1);
|
|
30
|
+
const vents = [];
|
|
31
|
+
for (let i = 1; i <= ventCount; i++) {
|
|
32
|
+
const z = -indoorHeight/2 + i * ventSpacing;
|
|
33
|
+
const vent = box(indoorWidth - 20, 2, 3, true)
|
|
34
|
+
.color('#333333')
|
|
35
|
+
.attachTo(frontPanel, 'front', 'center', [0, -1, z]);
|
|
36
|
+
vents.push(vent);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Display panel
|
|
40
|
+
const display = box(15, 1, 6, true)
|
|
41
|
+
.color('#1a1a2e')
|
|
42
|
+
.attachTo(frontPanel, 'front', 'center', [indoorWidth/4, -1, indoorHeight/4]);
|
|
43
|
+
|
|
44
|
+
// LED indicator
|
|
45
|
+
const led = sphere(1.5).color('#00ff00')
|
|
46
|
+
.attachTo(display, 'front', 'front', [0, -1, -2]);
|
|
47
|
+
|
|
48
|
+
// Mounting bracket
|
|
49
|
+
const bracket = box(indoorWidth - 10, 3, 5)
|
|
50
|
+
.color('#888888')
|
|
51
|
+
.attachTo(indoorBody, 'back', 'front');
|
|
52
|
+
|
|
53
|
+
// --- OUTDOOR UNIT (on BACK of wall, +Y side) ---
|
|
54
|
+
const outdoorBody = box(outdoorWidth, outdoorDepth, outdoorHeight, true)
|
|
55
|
+
.color('#e8e8e8')
|
|
56
|
+
.attachTo(wall, 'back', 'front', [0, 0, 10]); // Raised slightly
|
|
57
|
+
|
|
58
|
+
// Big fan housing - CYLINDER MUST BE ORIENTED ALONG Y FIRST!
|
|
59
|
+
const fanRadius = outdoorHeight * 0.35;
|
|
60
|
+
const fanDepth = outdoorDepth - 8;
|
|
61
|
+
const fanHousing = cylinder(fanDepth, fanRadius)
|
|
62
|
+
.pointAlong([0, 1, 0]) // Orient along Y axis (perpendicular to wall)
|
|
63
|
+
.color('#555555')
|
|
64
|
+
.attachTo(outdoorBody, 'front', 'back'); // Cylinder's back face at outdoor body's front
|
|
65
|
+
|
|
66
|
+
// Fan blades
|
|
67
|
+
const fanBlade = cylinder(3, fanRadius - 3)
|
|
68
|
+
.pointAlong([0, 1, 0])
|
|
69
|
+
.color('#222222')
|
|
70
|
+
.attachTo(fanHousing, 'front', 'front');
|
|
71
|
+
|
|
72
|
+
// Fan grille (concentric rings)
|
|
73
|
+
const grilleInner = circle2d(fanRadius * 0.4).color('#666666');
|
|
74
|
+
const grilleMiddle = circle2d(fanRadius * 0.7)
|
|
75
|
+
.subtract(circle2d(fanRadius * 0.6))
|
|
76
|
+
.color('#666666');
|
|
77
|
+
const grilleOuter = circle2d(fanRadius * 0.9)
|
|
78
|
+
.subtract(circle2d(fanRadius * 0.85))
|
|
79
|
+
.color('#666666');
|
|
80
|
+
const grille = union2d(grilleInner, grilleMiddle, grilleOuter)
|
|
81
|
+
.extrude(0.5)
|
|
82
|
+
.attachTo(fanBlade, 'front', 'front');
|
|
83
|
+
|
|
84
|
+
// Heat exchanger fins (on side, vertical)
|
|
85
|
+
const finCount = 8;
|
|
86
|
+
const fins = [];
|
|
87
|
+
for (let i = 0; i < finCount; i++) {
|
|
88
|
+
const fin = box(0.5, outdoorDepth - 8, outdoorHeight - 10, true)
|
|
89
|
+
.color('#cc8844')
|
|
90
|
+
.attachTo(outdoorBody, 'right', 'center', [outdoorWidth/2 - 3 + i * 3, 0, 0]);
|
|
91
|
+
fins.push(fin);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Outdoor unit legs
|
|
95
|
+
const legH = 8;
|
|
96
|
+
const leg1 = box(8, 8, legH).color('#444444')
|
|
97
|
+
.attachTo(outdoorBody, 'bottom', 'top', [outdoorWidth/3, outdoorDepth/3, -legH/2]);
|
|
98
|
+
const leg2 = box(8, 8, legH).color('#444444')
|
|
99
|
+
.attachTo(outdoorBody, 'bottom', 'top', [-outdoorWidth/3, outdoorDepth/3, -legH/2]);
|
|
100
|
+
|
|
101
|
+
// --- CONNECTING PIPES ---
|
|
102
|
+
const pipeR = 4;
|
|
103
|
+
|
|
104
|
+
// Get bounding boxes for precise pipe routing
|
|
105
|
+
const indoorBB = indoorBody.boundingBox();
|
|
106
|
+
const outdoorBB = outdoorBody.boundingBox();
|
|
107
|
+
|
|
108
|
+
// Pipe exit point: bottom-back of indoor unit
|
|
109
|
+
const exitX = -indoorWidth/3;
|
|
110
|
+
const exitY = indoorBB.min[1]; // Back of indoor unit (most negative Y)
|
|
111
|
+
const exitZ = indoorBB.min[2] + 8; // Near bottom
|
|
112
|
+
|
|
113
|
+
// Pipe entry point: bottom-front of outdoor unit
|
|
114
|
+
const entryX = outdoorWidth/3;
|
|
115
|
+
const entryY = outdoorBB.max[1]; // Front of outdoor unit (most positive Y)
|
|
116
|
+
const entryZ = outdoorBB.min[2] + 12;
|
|
117
|
+
|
|
118
|
+
// Segment 1: Drop down from indoor unit
|
|
119
|
+
const dropH = 20;
|
|
120
|
+
const pipe1 = cylinder(dropH, pipeR)
|
|
121
|
+
.pointAlong([0, 0, -1]) // Pointing down
|
|
122
|
+
.translate(exitX, exitY + 2, exitZ)
|
|
123
|
+
.color('#c4a066');
|
|
124
|
+
|
|
125
|
+
// Segment 2: Horizontal through wall
|
|
126
|
+
const midZ = exitZ - dropH;
|
|
127
|
+
const pipe2Len = entryY - exitY + 4; // From indoor exit to outdoor entry
|
|
128
|
+
const pipe2 = cylinder(pipe2Len, pipeR)
|
|
129
|
+
.pointAlong([0, 1, 0]) // Pointing toward outdoor (positive Y)
|
|
130
|
+
.translate(exitX, exitY + pipe2Len/2, midZ)
|
|
131
|
+
.color('#c4a066');
|
|
132
|
+
|
|
133
|
+
// Segment 3: Rise up to outdoor unit
|
|
134
|
+
const riseH = entryZ - midZ;
|
|
135
|
+
const pipe3 = cylinder(riseH, pipeR)
|
|
136
|
+
.pointAlong([0, 0, 1]) // Pointing up
|
|
137
|
+
.translate(entryX, entryY - 2, midZ)
|
|
138
|
+
.color('#c4a066');
|
|
139
|
+
|
|
140
|
+
// Segment 4: Connect into outdoor unit
|
|
141
|
+
const pipe4 = cylinder(12, pipeR)
|
|
142
|
+
.pointAlong([0, -1, 0]) // Pointing toward outdoor unit
|
|
143
|
+
.translate(entryX, entryY + 6, entryZ)
|
|
144
|
+
.color('#c4a066');
|
|
145
|
+
|
|
146
|
+
// --- Return as named objects ---
|
|
147
|
+
return [
|
|
148
|
+
{ name: "Wall", shape: wall },
|
|
149
|
+
|
|
150
|
+
// Indoor unit
|
|
151
|
+
{ name: "Indoor Body", shape: indoorBody },
|
|
152
|
+
{ name: "Front Panel", shape: frontPanel },
|
|
153
|
+
{ name: "Vents", group: vents.map((v, i) => ({ name: `Vent ${i + 1}`, shape: v })) },
|
|
154
|
+
{ name: "Display", shape: display },
|
|
155
|
+
{ name: "LED", shape: led },
|
|
156
|
+
{ name: "Mounting Bracket", shape: bracket },
|
|
157
|
+
|
|
158
|
+
// Outdoor unit
|
|
159
|
+
{ name: "Outdoor Body", shape: outdoorBody },
|
|
160
|
+
{ name: "Fan Housing", shape: fanHousing },
|
|
161
|
+
{ name: "Fan Blades", shape: fanBlade },
|
|
162
|
+
{ name: "Fan Grille", shape: grille },
|
|
163
|
+
{ name: "Heat Fins", group: fins.map((f, i) => ({ name: `Fin ${i + 1}`, shape: f })) },
|
|
164
|
+
{ name: "Legs", group: [
|
|
165
|
+
{ name: "Left Leg", shape: leg1 },
|
|
166
|
+
{ name: "Right Leg", shape: leg2 }
|
|
167
|
+
] },
|
|
168
|
+
|
|
169
|
+
// Pipes
|
|
170
|
+
{ name: "Pipe 1 (drop)", shape: pipe1 },
|
|
171
|
+
{ name: "Pipe 2 (horizontal)", shape: pipe2 },
|
|
172
|
+
{ name: "Pipe 3 (rise)", shape: pipe3 },
|
|
173
|
+
{ name: "Pipe 4 (connect)", shape: pipe4 },
|
|
174
|
+
];
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// Home AC Unit V3 - Fixed pipe connections to properly enter indoor unit
|
|
2
|
+
// Coordinate system: Z-up (X=left/right, Y=forward/back, Z=up/down)
|
|
3
|
+
// Front (-Y) = indoor side, Back (+Y) = outdoor side
|
|
4
|
+
|
|
5
|
+
// Parameters
|
|
6
|
+
const wallThick = param("Wall Thickness", 150, { min: 100, max: 300, unit: "mm" });
|
|
7
|
+
const indoorW = param("Indoor Width", 700, { min: 500, max: 1000, unit: "mm" });
|
|
8
|
+
const indoorH = param("Indoor Height", 250, { min: 180, max: 350, unit: "mm" });
|
|
9
|
+
const indoorD = param("Indoor Depth", 200, { min: 150, max: 300, unit: "mm" });
|
|
10
|
+
const outdoorW = param("Outdoor Width", 800, { min: 600, max: 1200, unit: "mm" });
|
|
11
|
+
const outdoorH = param("Outdoor Height", 550, { min: 400, max: 700, unit: "mm" });
|
|
12
|
+
const outdoorD = param("Outdoor Depth", 300, { min: 200, max: 400, unit: "mm" });
|
|
13
|
+
const mountHeight = param("Mount Height", 2000, { min: 1500, max: 2500, unit: "mm" });
|
|
14
|
+
const pipeSpacing = param("Pipe Spacing", 80, { min: 50, max: 150, unit: "mm" });
|
|
15
|
+
|
|
16
|
+
// --- Wall (vertical, separates indoor and outdoor) ---
|
|
17
|
+
const wallW = Math.max(indoorW, outdoorW) + 200;
|
|
18
|
+
const wallH = 2500;
|
|
19
|
+
const wall = box(wallW, wallThick, wallH, true)
|
|
20
|
+
.translate(0, 0, wallH / 2)
|
|
21
|
+
.color('#E8E4D9');
|
|
22
|
+
|
|
23
|
+
// --- Indoor Unit (on the -Y side of wall) ---
|
|
24
|
+
// Position: back face flush with wall front (Y = -wallThick/2), centered at mountHeight
|
|
25
|
+
const indoorUnitY = -wallThick/2 - indoorD/2;
|
|
26
|
+
const indoorUnitZ = mountHeight;
|
|
27
|
+
|
|
28
|
+
// Main body
|
|
29
|
+
const indoorBody = box(indoorW, indoorD, indoorH, true)
|
|
30
|
+
.color('#F5F5F0')
|
|
31
|
+
.translate(0, indoorUnitY, indoorUnitZ);
|
|
32
|
+
|
|
33
|
+
// Front panel with vent slats
|
|
34
|
+
const ventPanel = box(indoorW - 20, 5, indoorH * 0.6, true)
|
|
35
|
+
.color('#E0E0E0')
|
|
36
|
+
.translate(0, indoorUnitY - indoorD/2 - 2, indoorUnitZ - indoorH * 0.15);
|
|
37
|
+
|
|
38
|
+
// Top vent (air outlet)
|
|
39
|
+
const topVent = box(indoorW - 40, indoorD - 10, 15, true)
|
|
40
|
+
.color('#D0D0D0')
|
|
41
|
+
.translate(0, indoorUnitY, indoorUnitZ + indoorH/2 - 5);
|
|
42
|
+
|
|
43
|
+
// Display panel
|
|
44
|
+
const display = box(80, 3, 30, true)
|
|
45
|
+
.color('#1a1a2e')
|
|
46
|
+
.translate(indoorW * 0.3, indoorUnitY - indoorD/2 - 1, indoorUnitZ + indoorH * 0.25);
|
|
47
|
+
|
|
48
|
+
// LED indicator
|
|
49
|
+
const led = cylinder(2, 3, undefined, undefined, true)
|
|
50
|
+
.color('#00ff00')
|
|
51
|
+
.pointAlong([0, -1, 0])
|
|
52
|
+
.translate(indoorW * 0.3, indoorUnitY - indoorD/2 - 3, indoorUnitZ + indoorH * 0.25);
|
|
53
|
+
|
|
54
|
+
// Indoor mounting brackets (L-brackets on wall)
|
|
55
|
+
const bracketThick = 10;
|
|
56
|
+
const bracketW = 60;
|
|
57
|
+
const bracketV = box(bracketThick, bracketW, 100, true).color('#606060');
|
|
58
|
+
const bracketH = box(60, bracketThick, bracketW, true).color('#606060')
|
|
59
|
+
.translate(0, bracketW/2, -50 + bracketThick/2);
|
|
60
|
+
|
|
61
|
+
const indoorBracketL = union(bracketV, bracketH)
|
|
62
|
+
.translate(-indoorW/2 + 80, -wallThick/2 - bracketThick/2, mountHeight - indoorH/2);
|
|
63
|
+
|
|
64
|
+
const indoorBracketR = union(bracketV, bracketH)
|
|
65
|
+
.translate(indoorW/2 - 80, -wallThick/2 - bracketThick/2, mountHeight - indoorH/2);
|
|
66
|
+
|
|
67
|
+
// --- Outdoor Unit (on the +Y side of wall) ---
|
|
68
|
+
// Position: front face flush with wall back (Y = wallThick/2)
|
|
69
|
+
const outdoorUnitY = wallThick/2 + outdoorD/2;
|
|
70
|
+
const outdoorUnitZ = mountHeight - (outdoorH - indoorH)/2;
|
|
71
|
+
|
|
72
|
+
// Main body
|
|
73
|
+
const outdoorBody = box(outdoorW, outdoorD, outdoorH, true)
|
|
74
|
+
.color('#FFFFFF')
|
|
75
|
+
.translate(0, outdoorUnitY, outdoorUnitZ);
|
|
76
|
+
|
|
77
|
+
// Fan grill - vertical cylinder facing forward (along -Y)
|
|
78
|
+
const fanRadius = Math.min(outdoorW, outdoorH) * 0.35;
|
|
79
|
+
const fanGrill = cylinder(10, fanRadius, fanRadius, undefined, true)
|
|
80
|
+
.color('#404040')
|
|
81
|
+
.pointAlong([0, -1, 0])
|
|
82
|
+
.translate(0, outdoorUnitY + outdoorD/2 + 5, outdoorUnitZ);
|
|
83
|
+
|
|
84
|
+
// Fan blades
|
|
85
|
+
const bladeCount = 5;
|
|
86
|
+
const fanBlades = [];
|
|
87
|
+
for (let i = 0; i < bladeCount; i++) {
|
|
88
|
+
const angle = (i / bladeCount) * 360;
|
|
89
|
+
const blade = box(fanRadius * 0.7, 5, 30, true)
|
|
90
|
+
.color('#333333')
|
|
91
|
+
.rotate(angle, 0, 0)
|
|
92
|
+
.translate(0, outdoorUnitY + outdoorD/2 + 5, outdoorUnitZ);
|
|
93
|
+
fanBlades.push(blade);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Side vents
|
|
97
|
+
const sideVents = [];
|
|
98
|
+
for (let i = 0; i < 6; i++) {
|
|
99
|
+
const y = (i - 2.5) * (outdoorH * 0.6 / 5);
|
|
100
|
+
const sv = box(5, outdoorD - 20, 12, true)
|
|
101
|
+
.color('#C0C0C0')
|
|
102
|
+
.translate(outdoorW/2 + 2, outdoorUnitY, outdoorUnitZ + y);
|
|
103
|
+
sideVents.push(sv);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Outdoor mounting feet
|
|
107
|
+
const footW = 100;
|
|
108
|
+
const footD = 15;
|
|
109
|
+
const footH = 40;
|
|
110
|
+
|
|
111
|
+
const leftFoot = box(footD, outdoorD - 20, footH, true)
|
|
112
|
+
.color('#505050')
|
|
113
|
+
.translate(-outdoorW/2 + 100, outdoorUnitY, outdoorUnitZ - outdoorH/2 - footH/2);
|
|
114
|
+
|
|
115
|
+
const rightFoot = box(footD, outdoorD - 20, footH, true)
|
|
116
|
+
.color('#505050')
|
|
117
|
+
.translate(outdoorW/2 - 100, outdoorUnitY, outdoorUnitZ - outdoorH/2 - footH/2);
|
|
118
|
+
|
|
119
|
+
// Wall mounting plate for outdoor unit
|
|
120
|
+
const mountPlate = box(200, 10, 200, true)
|
|
121
|
+
.color('#505050')
|
|
122
|
+
.translate(0, wallThick/2 + 5, outdoorUnitZ);
|
|
123
|
+
|
|
124
|
+
// --- Connecting Pipes ---
|
|
125
|
+
// Two refrigerant lines going from outdoor unit, through wall, into indoor unit
|
|
126
|
+
const pipeR = 12;
|
|
127
|
+
|
|
128
|
+
// Pipe Z position - aligned with bottom portion of indoor unit
|
|
129
|
+
const pipeZ = mountHeight - indoorH/2 + 50;
|
|
130
|
+
|
|
131
|
+
// Calculate Y positions for pipe segments
|
|
132
|
+
// Indoor unit back face is at Y = indoorUnitY + indoorD/2 = -75 + 100 = 25? No...
|
|
133
|
+
// indoorUnitY = -75 - 100 = -175
|
|
134
|
+
// Indoor extends from Y=-275 (front) to Y=-75 (back, against wall)
|
|
135
|
+
// Wall is at Y=-75 to Y=+75
|
|
136
|
+
// Outdoor extends from Y=+75 (front, facing wall) to Y=+375 (back)
|
|
137
|
+
|
|
138
|
+
// Pipe 1 - the larger insulated line
|
|
139
|
+
// Outdoor segment: from outdoor front (Y=75) extending backward into unit
|
|
140
|
+
const pipe1Outdoor = cylinder(200, pipeR + 6, undefined, undefined, true)
|
|
141
|
+
.pointAlong([0, -1, 0]) // Points toward -Y (toward wall)
|
|
142
|
+
.color('#8B4513') // Copper
|
|
143
|
+
.translate(-pipeSpacing/2, outdoorUnitY - outdoorD/2 - 50, pipeZ);
|
|
144
|
+
|
|
145
|
+
// Wall segment: through the wall thickness
|
|
146
|
+
const pipe1Wall = cylinder(wallThick + 20, pipeR + 8, undefined, undefined, true)
|
|
147
|
+
.pointAlong([0, -1, 0])
|
|
148
|
+
.color('#1a5f7a') // Blue insulation
|
|
149
|
+
.translate(-pipeSpacing/2, 0, pipeZ);
|
|
150
|
+
|
|
151
|
+
// Indoor segment: from wall (Y=-75) extending into indoor unit (to Y=-175 center)
|
|
152
|
+
const pipe1Indoor = cylinder(120, pipeR + 8, undefined, undefined, true)
|
|
153
|
+
.pointAlong([0, -1, 0]) // Points toward -Y (into indoor unit)
|
|
154
|
+
.color('#1a5f7a')
|
|
155
|
+
.translate(-pipeSpacing/2, indoorUnitY + indoorD/2 - 60, pipeZ);
|
|
156
|
+
|
|
157
|
+
// Pipe 2 - smaller line
|
|
158
|
+
const pipe2Outdoor = cylinder(200, pipeR, undefined, undefined, true)
|
|
159
|
+
.pointAlong([0, -1, 0])
|
|
160
|
+
.color('#8B4513')
|
|
161
|
+
.translate(pipeSpacing/2, outdoorUnitY - outdoorD/2 - 50, pipeZ);
|
|
162
|
+
|
|
163
|
+
const pipe2Wall = cylinder(wallThick + 20, pipeR, undefined, undefined, true)
|
|
164
|
+
.pointAlong([0, -1, 0])
|
|
165
|
+
.color('#1a5f7a')
|
|
166
|
+
.translate(pipeSpacing/2, 0, pipeZ);
|
|
167
|
+
|
|
168
|
+
const pipe2Indoor = cylinder(120, pipeR, undefined, undefined, true)
|
|
169
|
+
.pointAlong([0, -1, 0])
|
|
170
|
+
.color('#1a5f7a')
|
|
171
|
+
.translate(pipeSpacing/2, indoorUnitY + indoorD/2 - 60, pipeZ);
|
|
172
|
+
|
|
173
|
+
// Wall sleeves
|
|
174
|
+
const sleeve1 = cylinder(wallThick + 10, pipeR + 15, undefined, undefined, true)
|
|
175
|
+
.pointAlong([0, -1, 0])
|
|
176
|
+
.color('#888888')
|
|
177
|
+
.translate(-pipeSpacing/2, 0, pipeZ);
|
|
178
|
+
|
|
179
|
+
const sleeve2 = cylinder(wallThick + 10, pipeR + 10, undefined, undefined, true)
|
|
180
|
+
.pointAlong([0, -1, 0])
|
|
181
|
+
.color('#888888')
|
|
182
|
+
.translate(pipeSpacing/2, 0, pipeZ);
|
|
183
|
+
|
|
184
|
+
// Connection points (visual only - where pipes enter the units)
|
|
185
|
+
const conn1Indoor = cylinder(20, pipeR + 10, undefined, undefined, true)
|
|
186
|
+
.pointAlong([0, -1, 0])
|
|
187
|
+
.color('#606060')
|
|
188
|
+
.translate(-pipeSpacing/2, indoorUnitY + indoorD/2, pipeZ);
|
|
189
|
+
|
|
190
|
+
const conn1Outdoor = cylinder(20, pipeR + 8, undefined, undefined, true)
|
|
191
|
+
.pointAlong([0, 1, 0])
|
|
192
|
+
.color('#606060')
|
|
193
|
+
.translate(-pipeSpacing/2, outdoorUnitY - outdoorD/2, pipeZ);
|
|
194
|
+
|
|
195
|
+
const conn2Indoor = cylinder(20, pipeR + 4, undefined, undefined, true)
|
|
196
|
+
.pointAlong([0, -1, 0])
|
|
197
|
+
.color('#606060')
|
|
198
|
+
.translate(pipeSpacing/2, indoorUnitY + indoorD/2, pipeZ);
|
|
199
|
+
|
|
200
|
+
const conn2Outdoor = cylinder(20, pipeR + 2, undefined, undefined, true)
|
|
201
|
+
.pointAlong([0, 1, 0])
|
|
202
|
+
.color('#606060')
|
|
203
|
+
.translate(pipeSpacing/2, outdoorUnitY - outdoorD/2, pipeZ);
|
|
204
|
+
|
|
205
|
+
return [
|
|
206
|
+
{ name: "Wall", shape: wall },
|
|
207
|
+
{ name: "Indoor Brackets", group: [
|
|
208
|
+
{ name: "Left Bracket", shape: indoorBracketL },
|
|
209
|
+
{ name: "Right Bracket", shape: indoorBracketR }
|
|
210
|
+
] },
|
|
211
|
+
{ name: "Indoor Unit Body", shape: indoorBody },
|
|
212
|
+
{ name: "Indoor Vent", shape: ventPanel },
|
|
213
|
+
{ name: "Indoor Top Vent", shape: topVent },
|
|
214
|
+
{ name: "Display Panel", shape: display },
|
|
215
|
+
{ name: "Power LED", shape: led },
|
|
216
|
+
{ name: "Outdoor Mount Plate", shape: mountPlate },
|
|
217
|
+
{ name: "Outdoor Unit Body", shape: outdoorBody },
|
|
218
|
+
{ name: "Fan Grill", shape: fanGrill },
|
|
219
|
+
{ name: "Fan Blades", group: fanBlades.map((b, i) => ({ name: `Blade ${i + 1}`, shape: b })) },
|
|
220
|
+
{ name: "Side Vents", group: sideVents.map((v, i) => ({ name: `Vent ${i + 1}`, shape: v })) },
|
|
221
|
+
{ name: "Outdoor Feet", group: [
|
|
222
|
+
{ name: "Left Foot", shape: leftFoot },
|
|
223
|
+
{ name: "Right Foot", shape: rightFoot }
|
|
224
|
+
] },
|
|
225
|
+
{ name: "Pipe 1 (Outdoor)", shape: pipe1Outdoor },
|
|
226
|
+
{ name: "Pipe 1 (Wall)", shape: pipe1Wall },
|
|
227
|
+
{ name: "Pipe 1 (Indoor)", shape: pipe1Indoor },
|
|
228
|
+
{ name: "Pipe 2 (Outdoor)", shape: pipe2Outdoor },
|
|
229
|
+
{ name: "Pipe 2 (Wall)", shape: pipe2Wall },
|
|
230
|
+
{ name: "Pipe 2 (Indoor)", shape: pipe2Indoor },
|
|
231
|
+
{ name: "Wall Sleeves", shape: union(sleeve1, sleeve2) },
|
|
232
|
+
{ name: "Conn Indoor 1", shape: conn1Indoor },
|
|
233
|
+
{ name: "Conn Outdoor 1", shape: conn1Outdoor },
|
|
234
|
+
{ name: "Conn Indoor 2", shape: conn2Indoor },
|
|
235
|
+
{ name: "Conn Outdoor 2", shape: conn2Outdoor },
|
|
236
|
+
];
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Home AC Unit V3 - Fixed positioning
|
|
2
|
+
// Indoor unit on front of wall, outdoor unit on back, connected through wall
|
|
3
|
+
|
|
4
|
+
// Parameters
|
|
5
|
+
const wallThick = param("Wall Thickness", 15, { min: 10, max: 30, unit: "mm" });
|
|
6
|
+
const wallWidth = param("Wall Width", 200, { min: 150, max: 300, unit: "mm" });
|
|
7
|
+
const wallHeight = param("Wall Height", 150, { min: 100, max: 250, unit: "mm" });
|
|
8
|
+
|
|
9
|
+
// Indoor unit parameters
|
|
10
|
+
const indoorW = param("Indoor Width", 70, { min: 50, max: 100, unit: "mm" });
|
|
11
|
+
const indoorD = param("Indoor Depth", 20, { min: 15, max: 35, unit: "mm" });
|
|
12
|
+
const indoorH = param("Indoor Height", 28, { min: 20, max: 45, unit: "mm" });
|
|
13
|
+
|
|
14
|
+
// Outdoor unit parameters
|
|
15
|
+
const outdoorW = param("Outdoor Width", 75, { min: 55, max: 110, unit: "mm" });
|
|
16
|
+
const outdoorD = param("Outdoor Depth", 30, { min: 20, max: 50, unit: "mm" });
|
|
17
|
+
const outdoorH = param("Outdoor Height", 55, { min: 35, max: 85, unit: "mm" });
|
|
18
|
+
const fanR = param("Fan Radius", 20, { min: 12, max: 30, unit: "mm" });
|
|
19
|
+
|
|
20
|
+
// Pipe parameters
|
|
21
|
+
const pipeR = param("Pipe Radius", 4, { min: 2, max: 8, unit: "mm" });
|
|
22
|
+
|
|
23
|
+
// === Build the wall (NOT centered - easier to position against) ===
|
|
24
|
+
const wall = box(wallWidth, wallThick, wallHeight).color('#999999');
|
|
25
|
+
|
|
26
|
+
// === Indoor Unit (mounted on FRONT of wall) ===
|
|
27
|
+
// Main body
|
|
28
|
+
const indoorMain = box(indoorW, indoorD, indoorH).color('#e8e8e8');
|
|
29
|
+
|
|
30
|
+
// Vent grille at bottom
|
|
31
|
+
const vent = rect(indoorW - 8, 4).extrude(2).color('#555555')
|
|
32
|
+
.translate(indoorW / 2, 0, -indoorH / 2 + 5);
|
|
33
|
+
const indoorWithVent = union(indoorMain, vent);
|
|
34
|
+
|
|
35
|
+
// Position: attach to front of wall, offset outward
|
|
36
|
+
const indoor = indoorWithVent.color('#f0f0f0')
|
|
37
|
+
.attachTo(wall, 'front', 'back', [0, -2, 20]);
|
|
38
|
+
|
|
39
|
+
// Display panel - 2D rect extruded, oriented vertically
|
|
40
|
+
const display = rect(15, 8).extrude(1).color('#222222')
|
|
41
|
+
.rotate(90, 0, 0) // Rotate to stand upright
|
|
42
|
+
.attachTo(indoor, 'front', 'back', [indoorW / 2 - 15, -1, indoorH / 2 - 8]);
|
|
43
|
+
|
|
44
|
+
// === Outdoor Unit (mounted on BACK of wall) ===
|
|
45
|
+
// Main body
|
|
46
|
+
const outdoorMain = box(outdoorW, outdoorD, outdoorH).color('#b8b8b8');
|
|
47
|
+
|
|
48
|
+
// Base/platform for outdoor unit
|
|
49
|
+
const outdoorBase = box(outdoorW + 8, outdoorD + 8, 6).color('#888888')
|
|
50
|
+
.attachTo(outdoorMain, 'bottom', 'top', [0, 0, -3]);
|
|
51
|
+
|
|
52
|
+
const outdoorWithBase = union(outdoorMain, outdoorBase);
|
|
53
|
+
|
|
54
|
+
// Position: attach to back of wall, offset outward
|
|
55
|
+
const outdoor = outdoorWithBase.color('#cccccc')
|
|
56
|
+
.attachTo(wall, 'back', 'front', [0, 2, 20]);
|
|
57
|
+
|
|
58
|
+
// === Fan Assembly (on front of outdoor unit, pointing outward) ===
|
|
59
|
+
// Fan grille - circle with hole, extruded
|
|
60
|
+
const grilleOuter = circle2d(fanR + 3).extrude(2).color('#666666');
|
|
61
|
+
const grilleInner = circle2d(fanR).extrude(3).color('#333333');
|
|
62
|
+
const grille = difference(grilleOuter, grilleInner);
|
|
63
|
+
|
|
64
|
+
// Rotate grille to be perpendicular to front face (facing outward along +Y)
|
|
65
|
+
const grilleRotated = grille.rotate(90, 0, 0)
|
|
66
|
+
.attachTo(outdoor, 'front', 'back', [0, -1, 0]);
|
|
67
|
+
|
|
68
|
+
// Fan blades - radial pattern
|
|
69
|
+
const blade = box(3, fanR - 4, 2).translate(0, (fanR - 4) / 2 + 2, 0).color('#555555');
|
|
70
|
+
const fan = circularPattern(blade, 5).color('#666666')
|
|
71
|
+
.rotate(90, 0, 0) // Rotate to stand upright in grille
|
|
72
|
+
.attachTo(outdoor, 'front', 'back', [0, -1, 0]);
|
|
73
|
+
|
|
74
|
+
// Fan hub - cylinder pointing outward
|
|
75
|
+
const hub = cylinder(8, fanR / 4).color('#444444')
|
|
76
|
+
.rotate(90, 0, 0) // Point along Y axis
|
|
77
|
+
.attachTo(outdoor, 'front', 'center', [0, -1, 0]);
|
|
78
|
+
|
|
79
|
+
// === Connector Pipes (through the wall) ===
|
|
80
|
+
// Get positions from the attached shapes
|
|
81
|
+
const indoorBB = indoor.boundingBox();
|
|
82
|
+
const outdoorBB = outdoor.boundingBox();
|
|
83
|
+
|
|
84
|
+
// Pipe goes from indoor back to outdoor front through wall
|
|
85
|
+
// Wall is at Y=0 to Y=wallThick, so pipe should span from indoor back to outdoor front
|
|
86
|
+
const pipeStartY = indoorBB.max[1]; // Back of indoor
|
|
87
|
+
const pipeEndY = outdoorBB.min[1]; // Front of outdoor
|
|
88
|
+
const pipeLen = pipeEndY - pipeStartY;
|
|
89
|
+
|
|
90
|
+
// Vertical offset on the units
|
|
91
|
+
const pipeZ = 20;
|
|
92
|
+
|
|
93
|
+
// Main refrigerant pipe - goes through wall along Y axis
|
|
94
|
+
const mainPipe = cylinder(pipeLen, pipeR)
|
|
95
|
+
.pointAlong([0, 1, 0]) // Along Y axis
|
|
96
|
+
.translate(0, (pipeStartY + pipeEndY) / 2, pipeZ)
|
|
97
|
+
.color('#aa6644');
|
|
98
|
+
|
|
99
|
+
// Second refrigerant line - offset to the right
|
|
100
|
+
const line2 = cylinder(pipeLen, pipeR * 0.7)
|
|
101
|
+
.pointAlong([0, 1, 0])
|
|
102
|
+
.translate(pipeR * 2 + 2, (pipeStartY + pipeEndY) / 2, pipeZ)
|
|
103
|
+
.color('#aa6644');
|
|
104
|
+
|
|
105
|
+
// Condensate drain line - lower down
|
|
106
|
+
const drain = cylinder(pipeLen, pipeR * 0.6)
|
|
107
|
+
.pointAlong([0, 1, 0])
|
|
108
|
+
.translate(0, (pipeStartY + pipeEndY) / 2, pipeZ - pipeR * 2 - 2)
|
|
109
|
+
.color('#44aa66');
|
|
110
|
+
|
|
111
|
+
// Return all objects as separate items to preserve colors
|
|
112
|
+
return [
|
|
113
|
+
{ name: "Wall", shape: wall },
|
|
114
|
+
{ name: "Indoor Unit", shape: indoor },
|
|
115
|
+
{ name: "Display", shape: display },
|
|
116
|
+
{ name: "Outdoor Unit", shape: outdoor },
|
|
117
|
+
{ name: "Fan Grille", shape: grilleRotated },
|
|
118
|
+
{ name: "Fan Blades", shape: fan },
|
|
119
|
+
{ name: "Fan Hub", shape: hub },
|
|
120
|
+
{ name: "Main Pipe", shape: mainPipe },
|
|
121
|
+
{ name: "Refrigerant Line", shape: line2 },
|
|
122
|
+
{ name: "Drain Line", shape: drain },
|
|
123
|
+
];
|