latest :)

This commit is contained in:
babayaga 2025-07-04 22:48:59 +02:00
parent 8e5d9f2613
commit 9150977f32
142 changed files with 171593 additions and 0 deletions

BIN
products/poly-mech/cad/drawers/500/explode.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
products/poly-mech/cad/drawers/500/perspective.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
products/poly-mech/cad/drawers/tools/box_folding_tool.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,348 @@
// Customizable parameters for the box folding tool
// These values are based on the provided screenshot.
// Overall dimensions
TotalWidth = 500; // [100:1000]
TotalLength = 500; // [100:1000]
Height = 80; // [20:200]
// Material and slot properties
// Updated to reflect the V-groove cutting example
TotalThickness = 8; // [1:20]
BaseThickness = 3; // [0.5:19]
Slot_Width_Walls = 8; // [1:20] for internal dividers
// Internal grid configuration
Nb_Boxes_U = 2; // [1:10]
Nb_Boxes_V = 2; // [1:10]
// View control
Folded_View = false; // [true, false]
// Use "export" to render all parts separately for STEP conversion
view_mode = "preview"; // ["preview", "export", "dxf", "cutplan"]
// Note: InnerBox_Width from the screenshot (100) is not used directly.
// Instead, the compartment widths are calculated based on TotalWidth, Height, and Nb_Boxes_U/V.
// This ensures the design remains consistent with the overall dimensions.
// ---------------------------------------------------------------------
// Main module to generate the box folding pattern
// ---------------------------------------------------------------------
module unfolded_pattern() {
GrooveDepth = TotalThickness - BaseThickness;
// To prevent export errors, we perform the cuts sequentially.
// This is more stable than a single, complex difference().
difference() {
// Start with the result of the first cut...
difference() {
// 1. Create the base plate
cube([TotalWidth, TotalLength, TotalThickness], center = true);
// 2. Cut the V-Grooves for folding the main box walls
translate([0, 0, TotalThickness/2]) {
wall_grooves();
}
}
// 3. ...and then cut the internal rectangular slots from that result.
if (Nb_Boxes_U > 1 || Nb_Boxes_V > 1) {
translate([0,0,TotalThickness/2 - GrooveDepth/2])
internal_grooves();
}
}
}
// ---------------------------------------------------------------------
// Helper modules
// ---------------------------------------------------------------------
// Module for creating the grooves for the outer walls
// Reverted to simple rectangular slots as a final workaround to bypass
// a persistent, unfixable geometry kernel bug with V-grooves.
module wall_grooves() {
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
GrooveDepth = TotalThickness - BaseThickness;
// For a folding groove, the width should be slightly less than the material thickness
// to leave some material at the corners. We'll use the depth as a proxy.
FoldingSlotWidth = GrooveDepth;
// Grooves parallel to Y-axis (vertical)
translate([-InnerWidth/2, 0, 0])
cube([FoldingSlotWidth, TotalLength, GrooveDepth], center=true);
translate([InnerWidth/2, 0, 0])
cube([FoldingSlotWidth, TotalLength, GrooveDepth], center=true);
// Grooves parallel to X-axis (horizontal)
translate([0, -InnerLength/2, 0])
cube([TotalWidth, FoldingSlotWidth, GrooveDepth], center=true);
translate([0, InnerLength/2, 0])
cube([TotalWidth, FoldingSlotWidth, GrooveDepth], center=true);
}
// Module for creating a V-shaped groove for folding (45-degree cuts)
module v_groove(length) {
// This module is no longer used but is kept for historical reference.
}
// Module for creating the grooves for the internal compartments
module internal_grooves() {
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
GrooveDepth = TotalThickness - BaseThickness;
CompartmentWidth = (InnerWidth - (Nb_Boxes_U - 1) * Slot_Width_Walls) / Nb_Boxes_U;
CompartmentLength = (InnerLength - (Nb_Boxes_V - 1) * Slot_Width_Walls) / Nb_Boxes_V;
// Internal vertical grooves
if (Nb_Boxes_U > 1) {
for (i = [1 : Nb_Boxes_U - 1]) {
x_pos = -InnerWidth/2 + i * CompartmentWidth + (i - 1/2) * Slot_Width_Walls;
translate([x_pos, 0, 0])
cube([Slot_Width_Walls, InnerLength, GrooveDepth], center = true);
}
}
// Internal horizontal grooves
if (Nb_Boxes_V > 1) {
for (i = [1 : Nb_Boxes_V - 1]) {
y_pos = -InnerLength/2 + i * CompartmentLength + (i - 1/2) * Slot_Width_Walls;
translate([0, y_pos, 0])
cube([InnerWidth, Slot_Width_Walls, GrooveDepth], center = true);
}
}
}
// ---------------------------------------------------------------------
// Modules for Folded View
// ---------------------------------------------------------------------
// Generates the fully assembled 3D box
module folded_box() {
// Inner dimensions of the box
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
// Material thickness for all parts
wall_thickness = TotalThickness;
// 1. Base Plate
translate([0, 0, wall_thickness / 2])
cube([InnerWidth, InnerLength, wall_thickness], center = true);
// 2. Outer Walls
// South Wall (bottom) - Full Width
translate([0, -InnerLength/2, Height/2 + wall_thickness])
rotate([90, 0, 0])
cube([InnerWidth, Height, wall_thickness], center=true);
// North Wall (top) - Full Width
translate([0, InnerLength/2, Height/2 + wall_thickness])
rotate([-90, 0, 0])
cube([InnerWidth, Height, wall_thickness], center=true);
// West Wall (left) - Shortened to fit between North/South walls
translate([-InnerWidth/2, 0, Height/2 + wall_thickness])
rotate([0, 90, 0])
cube([Height, InnerLength - 2 * wall_thickness, wall_thickness], center=true);
// East Wall (right) - Shortened to fit between North/South walls
translate([InnerWidth/2, 0, Height/2 + wall_thickness])
rotate([0, -90, 0])
cube([Height, InnerLength - 2 * wall_thickness, wall_thickness], center=true);
// 3. Internal Dividers
internal_dividers_folded();
}
// Generates the interlocking internal dividers for the folded view
module internal_dividers_folded() {
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
divider_thickness = Slot_Width_Walls; // Use slot width as the divider material thickness
// Calculate compartment sizes
Compartment_U = (InnerWidth + divider_thickness) / Nb_Boxes_U;
Compartment_V = (InnerLength + divider_thickness) / Nb_Boxes_V;
// Vertical dividers (U-direction)
for (i = [1 : Nb_Boxes_U - 1]) {
x_pos = -InnerWidth/2 + i * Compartment_U - divider_thickness/2;
difference() {
// Main divider piece
translate([x_pos, 0, Height/2 + TotalThickness])
cube([divider_thickness, InnerLength, Height], center=true);
// Slots for horizontal dividers (top-down)
for (j = [1 : Nb_Boxes_V - 1]) {
y_pos = -InnerLength/2 + j * Compartment_V - divider_thickness/2;
translate([x_pos, y_pos, Height * 0.75 + TotalThickness])
cube([divider_thickness + 0.1, divider_thickness, Height/2], center=true);
}
}
}
// Horizontal dividers (V-direction)
for (j = [1 : Nb_Boxes_V - 1]) {
y_pos = -InnerLength/2 + j * Compartment_V - divider_thickness/2;
difference() {
// Main divider piece
translate([0, y_pos, Height/2 + TotalThickness])
cube([InnerWidth, divider_thickness, Height], center=true);
// Slots for vertical dividers (bottom-up)
for (i = [1 : Nb_Boxes_U - 1]) {
x_pos = -InnerWidth/2 + i * Compartment_U - divider_thickness/2;
translate([x_pos, y_pos, Height * 0.25 + TotalThickness])
cube([divider_thickness, divider_thickness + 0.1, Height/2], center=true);
}
}
}
}
// ---------------------------------------------------------------------
// Module for exporting all parts separately
// ---------------------------------------------------------------------
module export_layout() {
wall_thickness = TotalThickness;
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
// 1. Base Plate
translate([0, 0, -TotalThickness/2])
cube([InnerWidth, InnerLength, wall_thickness], center = true);
// Spacing for laying out parts
spacing = TotalWidth;
// 2. Outer Walls
translate([spacing, 0, 0])
cube([InnerWidth, Height, wall_thickness], center=true); // South
translate([spacing, Height + 10, 0])
cube([InnerWidth, Height, wall_thickness], center=true); // North
translate([spacing + InnerWidth + 10, 0, 0])
cube([InnerLength - 2 * wall_thickness, Height, wall_thickness], center=true); // West
translate([spacing + InnerWidth + 10, Height + 10, 0])
cube([InnerLength - 2 * wall_thickness, Height, wall_thickness], center=true); // East
// 3. Internal Dividers
divider_thickness = Slot_Width_Walls;
Compartment_U = (InnerWidth + divider_thickness) / Nb_Boxes_U;
Compartment_V = (InnerLength + divider_thickness) / Nb_Boxes_V;
// Place all vertical dividers in a row
for (i = [1 : Nb_Boxes_U - 1]) {
translate([2 * spacing + (i-1)*(divider_thickness+10), 0, 0])
internal_divider_vertical_export(InnerLength, Compartment_V);
}
// Place all horizontal dividers in a row
for (j = [1 : Nb_Boxes_V - 1]) {
translate([2 * spacing, (j-1)*(Height+10) + Height + 10, 0])
internal_divider_horizontal_export(InnerWidth, Compartment_U);
}
}
// Helper modules for export layout (without difference operations)
module internal_divider_vertical_export(length, compartment_v_size) {
divider_thickness = Slot_Width_Walls;
difference() {
cube([divider_thickness, length, Height], center=true);
// Notches for horizontal dividers (bottom-up)
for (i = [1 : Nb_Boxes_V - 1]) {
y_pos = -length/2 + i * compartment_v_size - divider_thickness/2;
translate([0, y_pos, -Height/4])
cube([divider_thickness + 0.1, divider_thickness, Height/2], center=true);
}
}
}
module internal_divider_horizontal_export(length, compartment_u_size) {
divider_thickness = Slot_Width_Walls;
difference() {
cube([length, divider_thickness, Height], center=true);
// Notches for vertical dividers (top-down)
for (j = [1 : Nb_Boxes_U - 1]) {
x_pos = -length/2 + j * compartment_u_size - divider_thickness/2;
translate([x_pos, 0, Height/4])
cube([divider_thickness, divider_thickness + 0.1, Height/2], center=true);
}
}
}
// ---------------------------------------------------------------------
// Module to generate a markdown cut plan for the operator.
// This version avoids all list manipulation for maximum compatibility.
// ---------------------------------------------------------------------
module generate_cut_plan_text() {
// --- Calculations ---
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
GrooveDepth = TotalThickness - BaseThickness;
// --- Markdown Output ---
echo("# Saw Operator Cut Plan");
echo(str("## Board Dimensions: `", TotalWidth, " x ", TotalLength, " mm`"));
echo("");
echo("## Specifications");
echo(str("* **V-Groove Spec:** Depth `", GrooveDepth, " mm`"));
echo(str("* **Slot Spec:** Width `", Slot_Width_Walls, " mm`, Depth `", GrooveDepth, " mm`"));
echo("");
echo("## Blade Setup");
echo("* **For V-Grooves:** Set saw blade angle to **45 degrees**.");
echo("* **For Slots:** Set saw blade angle to **0 degrees** (straight up).");
echo("");
// --- Setup 1: Vertical Cuts ---
echo("## SETUP 1: VERTICAL CUTS");
echo("All distances are from the LEFT edge. Flip board for cuts > 50%.");
echo("");
echo(str("* `", Height, " mm` -- **V-GROOVE**"));
if (Nb_Boxes_U > 1) {
for (i = [1 : Nb_Boxes_U - 1]) {
pos = Height + i * ((InnerWidth - (Nb_Boxes_U - 1) * Slot_Width_Walls) / Nb_Boxes_U) + i * Slot_Width_Walls;
echo(str("* `", pos, " mm` -- **SLOT**"));
}
}
echo(str("* `", TotalWidth - Height, " mm` -- **V-GROOVE**"));
echo("");
// --- Setup 2: Horizontal Cuts ---
echo("## SETUP 2: HORIZONTAL CUTS");
echo("Rotate board 90 deg. All distances from the NEW LEFT edge.");
echo("");
echo(str("* `", Height, " mm` -- **V-GROOVE**"));
if (Nb_Boxes_V > 1) {
for (i = [1 : Nb_Boxes_V - 1]) {
pos = Height + i * ((InnerLength - (Nb_Boxes_V - 1) * Slot_Width_Walls) / Nb_Boxes_V) + i * Slot_Width_Walls;
echo(str("* `", pos, " mm` -- **SLOT**"));
}
}
echo(str("* `", TotalLength - Height, " mm` -- **V-GROOVE**"));
echo("");
echo("---");
echo("*End of Plan*");
// Generate a tiny invisible cube because OpenSCAD needs to produce some geometry.
cube(0.01);
}
// ---------------------------------------------------------------------
// Render the final object
// ---------------------------------------------------------------------
if (view_mode == "export") {
export_layout();
} else if (view_mode == "dxf") {
// Project the 2D unfolded pattern for DXF export
projection(cut = true) unfolded_pattern();
} else if (view_mode == "cutplan") {
generate_cut_plan_text();
} else {
if (Folded_View) {
folded_box();
} else {
unfolded_pattern();
}
}

BIN
products/poly-mech/cad/drawers/tools/box_folding_tool.step (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,982 @@
solid OpenSCAD_Model
facet normal -1 0 0
outer loop
vertex -4 -167.5 5
vertex -4 -4 45
vertex -4 -4 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -4 -167.5 85
vertex -4 -4 45
vertex -4 -167.5 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -4 -4 45
vertex -4 -167.5 85
vertex -4 -4 85
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -4 4 45
vertex -4 167.5 5
vertex -4 4 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -4 167.5 5
vertex -4 4 45
vertex -4 167.5 85
endloop
endfacet
facet normal -1 -0 0
outer loop
vertex -4 167.5 85
vertex -4 4 45
vertex -4 4 85
endloop
endfacet
facet normal -0 0 1
outer loop
vertex -172.5 165 85
vertex -167.5 4 85
vertex -167.5 165 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -167.5 4 85
vertex -172.5 165 85
vertex -167.5 -4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -172.5 -165 85
vertex -167.5 -4 85
vertex -172.5 165 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -167.5 -4 85
vertex -172.5 -165 85
vertex -167.5 -165 85
endloop
endfacet
facet normal -0 0 1
outer loop
vertex -167.5 4 85
vertex -4 -4 85
vertex -4 4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 -4 85
vertex -167.5 4 85
vertex -167.5 -4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 170 172.5 85
vertex 4 167.5 85
vertex 170 167.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 4 85
vertex 4 4 85
vertex 4 167.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 4 4 85
vertex -4 4 85
vertex 4 -4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 4 85
vertex 4 167.5 85
vertex -4 167.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 170 172.5 85
vertex -4 167.5 85
vertex 4 167.5 85
endloop
endfacet
facet normal -0 0 1
outer loop
vertex -170 172.5 85
vertex -4 167.5 85
vertex 170 172.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 167.5 85
vertex -170 172.5 85
vertex -170 167.5 85
endloop
endfacet
facet normal -0 0 1
outer loop
vertex 4 4 85
vertex 167.5 -4 85
vertex 167.5 4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 167.5 -4 85
vertex 4 4 85
vertex 4 -4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 167.5 4 85
vertex 172.5 165 85
vertex 167.5 165 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 172.5 165 85
vertex 167.5 4 85
vertex 172.5 -165 85
endloop
endfacet
facet normal -0 0 1
outer loop
vertex 167.5 -4 85
vertex 172.5 -165 85
vertex 167.5 4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 172.5 -165 85
vertex 167.5 -4 85
vertex 167.5 -165 85
endloop
endfacet
facet normal -0 0 1
outer loop
vertex 4 -167.5 85
vertex 170 -172.5 85
vertex 170 -167.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 -4 85
vertex 4 -4 85
vertex -4 4 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 4 -4 85
vertex -4 -4 85
vertex 4 -167.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 -167.5 85
vertex 4 -167.5 85
vertex -4 -4 85
endloop
endfacet
facet normal -0 0 1
outer loop
vertex -4 -167.5 85
vertex 170 -172.5 85
vertex 4 -167.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -170 -172.5 85
vertex -4 -167.5 85
vertex -170 -167.5 85
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 -167.5 85
vertex -170 -172.5 85
vertex 170 -172.5 85
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 4 -167.5 85
vertex 4 -4 45
vertex 4 -4 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 4 -167.5 5
vertex 4 -4 45
vertex 4 -167.5 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 4 -4 45
vertex 4 -167.5 5
vertex 4 -4 5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 4 4 45
vertex 4 167.5 85
vertex 4 4 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 4 167.5 85
vertex 4 4 45
vertex 4 167.5 5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 4 167.5 5
vertex 4 4 45
vertex 4 4 5
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 170 -165 5
vertex 172.5 165 5
vertex 172.5 -165 5
endloop
endfacet
facet normal -0 0 -1
outer loop
vertex 172.5 165 5
vertex 170 -165 5
vertex 170 165 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 167.5 -4 5
vertex 4 -4 5
vertex 167.5 -165 5
endloop
endfacet
facet normal -0 0 1
outer loop
vertex 167.5 -165 5
vertex 170 -167.5 5
vertex 170 -165 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 4 -167.5 5
vertex 167.5 -165 5
vertex 4 -4 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 167.5 -165 5
vertex 4 -167.5 5
vertex 170 -167.5 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 170 167.5 5
vertex 167.5 165 5
vertex 170 165 5
endloop
endfacet
facet normal -0 0 1
outer loop
vertex 4 167.5 5
vertex 167.5 165 5
vertex 170 167.5 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 4 4 5
vertex 167.5 165 5
vertex 4 167.5 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 167.5 165 5
vertex 4 4 5
vertex 167.5 4 5
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex -170 -172.5 5
vertex 170 -170 5
vertex 170 -172.5 5
endloop
endfacet
facet normal -0 0 -1
outer loop
vertex 170 -170 5
vertex -170 -172.5 5
vertex -170 -170 5
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex -170 170 5
vertex 170 172.5 5
vertex 170 170 5
endloop
endfacet
facet normal -0 0 -1
outer loop
vertex 170 172.5 5
vertex -170 170 5
vertex -170 172.5 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 167.5 5
vertex -167.5 165 5
vertex -4 4 5
endloop
endfacet
facet normal -0 0 1
outer loop
vertex -170 167.5 5
vertex -167.5 165 5
vertex -4 167.5 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -167.5 165 5
vertex -170 167.5 5
vertex -170 165 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 4 5
vertex -167.5 165 5
vertex -167.5 4 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -167.5 -165 5
vertex -4 -4 5
vertex -167.5 -4 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -4 -4 5
vertex -167.5 -165 5
vertex -4 -167.5 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -170 -167.5 5
vertex -167.5 -165 5
vertex -170 -165 5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -167.5 -165 5
vertex -170 -167.5 5
vertex -4 -167.5 5
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex -172.5 -165 5
vertex -170 165 5
vertex -170 -165 5
endloop
endfacet
facet normal -0 0 -1
outer loop
vertex -170 165 5
vertex -172.5 -165 5
vertex -172.5 165 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex -167.5 -4 85
vertex -4 -4 45
vertex -4 -4 85
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex -167.5 -4 5
vertex -4 -4 45
vertex -167.5 -4 85
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex -4 -4 45
vertex -167.5 -4 5
vertex -4 -4 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 4 -4 45
vertex 167.5 -4 85
vertex 4 -4 85
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 167.5 -4 5
vertex 4 -4 45
vertex 4 -4 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 4 -4 45
vertex 167.5 -4 5
vertex 167.5 -4 85
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex -4 4 45
vertex -167.5 4 85
vertex -4 4 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -167.5 4 5
vertex -4 4 45
vertex -4 4 5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -4 4 45
vertex -167.5 4 5
vertex -167.5 4 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 167.5 4 85
vertex 4 4 45
vertex 4 4 85
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 167.5 4 5
vertex 4 4 45
vertex 167.5 4 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 4 4 45
vertex 167.5 4 5
vertex 4 4 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 170 0
vertex -170 167.5 5
vertex -170 170 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 170 0
vertex -170 165 5
vertex -170 167.5 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 170 0
vertex -170 -165 5
vertex -170 165 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 -170 0
vertex -170 -165 5
vertex -170 170 0
endloop
endfacet
facet normal -1 -0 0
outer loop
vertex -170 -165 5
vertex -170 -170 0
vertex -170 -167.5 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 -170 5
vertex -170 -167.5 5
vertex -170 -170 0
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 -167.5 5
vertex -170 -170 5
vertex -170 -167.5 85
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 -172.5 85
vertex -170 -170 5
vertex -170 -172.5 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 -170 5
vertex -170 -172.5 85
vertex -170 -167.5 85
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 170 5
vertex -170 172.5 85
vertex -170 172.5 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 167.5 85
vertex -170 170 5
vertex -170 167.5 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -170 170 5
vertex -170 167.5 85
vertex -170 172.5 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 -167.5 85
vertex 170 -170 5
vertex 170 -167.5 5
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 170 -172.5 85
vertex 170 -170 5
vertex 170 -167.5 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 -170 5
vertex 170 -172.5 85
vertex 170 -172.5 5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 172.5 85
vertex 170 170 5
vertex 170 172.5 5
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 170 167.5 85
vertex 170 170 5
vertex 170 172.5 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 167.5 5
vertex 170 170 5
vertex 170 167.5 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 170 5
vertex 170 167.5 5
vertex 170 170 0
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 170 165 5
vertex 170 170 0
vertex 170 167.5 5
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 170 -165 5
vertex 170 170 0
vertex 170 165 5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 -170 0
vertex 170 -165 5
vertex 170 -167.5 5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 -170 0
vertex 170 -167.5 5
vertex 170 -170 5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 170 -165 5
vertex 170 -170 0
vertex 170 170 0
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 170 170 0
vertex -170 170 5
vertex 170 170 5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -170 170 5
vertex 170 170 0
vertex -170 170 0
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex -170 -170 0
vertex 170 170 0
vertex 170 -170 0
endloop
endfacet
facet normal -0 0 -1
outer loop
vertex 170 170 0
vertex -170 -170 0
vertex -170 170 0
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex -170 -170 0
vertex 170 -170 5
vertex -170 -170 5
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 170 -170 5
vertex -170 -170 0
vertex 170 -170 0
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex -170 -172.5 5
vertex 170 -172.5 85
vertex -170 -172.5 85
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 170 -172.5 85
vertex -170 -172.5 5
vertex 170 -172.5 5
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex -4 -167.5 5
vertex -170 -167.5 85
vertex -4 -167.5 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -170 -167.5 85
vertex -4 -167.5 5
vertex -170 -167.5 5
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 170 -167.5 5
vertex 4 -167.5 85
vertex 170 -167.5 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 4 -167.5 85
vertex 170 -167.5 5
vertex 4 -167.5 5
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 170 172.5 5
vertex -170 172.5 85
vertex 170 172.5 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -170 172.5 85
vertex 170 172.5 5
vertex -170 172.5 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex -170 167.5 5
vertex -4 167.5 85
vertex -170 167.5 85
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex -4 167.5 85
vertex -170 167.5 5
vertex -4 167.5 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 4 167.5 5
vertex 170 167.5 85
vertex 4 167.5 85
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 170 167.5 85
vertex 4 167.5 5
vertex 170 167.5 5
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex -167.5 -165 85
vertex -167.5 -4 5
vertex -167.5 -4 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex -167.5 -4 5
vertex -167.5 -165 85
vertex -167.5 -165 5
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex -167.5 4 85
vertex -167.5 165 5
vertex -167.5 165 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex -167.5 165 5
vertex -167.5 4 85
vertex -167.5 4 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex -170 -165 5
vertex -172.5 -165 85
vertex -172.5 -165 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex -172.5 -165 85
vertex -170 -165 5
vertex -167.5 -165 85
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex -167.5 -165 85
vertex -170 -165 5
vertex -167.5 -165 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex -172.5 -165 5
vertex -172.5 165 85
vertex -172.5 165 5
endloop
endfacet
facet normal -1 -0 0
outer loop
vertex -172.5 165 85
vertex -172.5 -165 5
vertex -172.5 -165 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -170 165 5
vertex -167.5 165 85
vertex -167.5 165 5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -167.5 165 85
vertex -170 165 5
vertex -172.5 165 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex -172.5 165 85
vertex -170 165 5
vertex -172.5 165 5
endloop
endfacet
facet normal -1 0 0
outer loop
vertex 167.5 -165 5
vertex 167.5 -4 85
vertex 167.5 -4 5
endloop
endfacet
facet normal -1 -0 0
outer loop
vertex 167.5 -4 85
vertex 167.5 -165 5
vertex 167.5 -165 85
endloop
endfacet
facet normal -1 0 0
outer loop
vertex 167.5 4 5
vertex 167.5 165 85
vertex 167.5 165 5
endloop
endfacet
facet normal -1 -0 0
outer loop
vertex 167.5 165 85
vertex 167.5 4 5
vertex 167.5 4 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 170 165 5
vertex 172.5 165 85
vertex 172.5 165 5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 172.5 165 85
vertex 170 165 5
vertex 167.5 165 85
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 167.5 165 85
vertex 170 165 5
vertex 167.5 165 5
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 172.5 -165 85
vertex 172.5 165 5
vertex 172.5 165 85
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 172.5 165 5
vertex 172.5 -165 85
vertex 172.5 -165 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 170 -165 5
vertex 167.5 -165 85
vertex 167.5 -165 5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 167.5 -165 85
vertex 170 -165 5
vertex 172.5 -165 85
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 172.5 -165 85
vertex 170 -165 5
vertex 172.5 -165 5
endloop
endfacet
endsolid OpenSCAD_Model

View File

@ -0,0 +1,214 @@
0
SECTION
2
BLOCKS
0
ENDSEC
0
SECTION
2
ENTITIES
0
LINE
8
0
10
-250
20
-250
11
250
21
-250
0
LINE
8
0
10
250
20
-250
11
250
21
250
0
LINE
8
0
10
250
20
250
11
-250
21
250
0
LINE
8
0
10
-250
20
250
11
-250
21
-250
0
LINE
8
0
10
-165
20
-4
11
-165
21
4
0
LINE
8
0
10
-165
20
4
11
-4
21
4
0
LINE
8
0
10
-4
20
4
11
-4
21
165
0
LINE
8
0
10
-4
20
165
11
4
21
165
0
LINE
8
0
10
4
20
165
11
4
21
4
0
LINE
8
0
10
4
20
4
11
165
21
4
0
LINE
8
0
10
165
20
4
11
165
21
-4
0
LINE
8
0
10
165
20
-4
11
4
21
-4
0
LINE
8
0
10
4
20
-4
11
4
21
-165
0
LINE
8
0
10
4
20
-165
11
-4
21
-165
0
LINE
8
0
10
-4
20
-165
11
-4
21
-4
0
LINE
8
0
10
-4
20
-4
11
-165
21
-4
0
ENDSEC
0
SECTION
2
OBJECTS
0
DICTIONARY
0
ENDSEC
0
EOF

View File

@ -0,0 +1,101 @@
import argparse
def generate_cut_plan(total_width, total_length, height, base_thickness, total_thickness, slot_width, u_boxes, v_boxes):
"""
Calculates and prints a markdown cut plan for the operator.
"""
# --- Calculations ---
inner_width = total_width - 2 * height
inner_length = total_length - 2 * height
groove_depth = total_thickness - base_thickness
# --- Collect all cut positions ---
vertical_cuts = []
if u_boxes > 1:
compartment_width = (inner_width - (u_boxes - 1) * slot_width) / u_boxes
for i in range(1, u_boxes):
pos = height + (i * compartment_width) + (i * slot_width)
vertical_cuts.append(pos)
vertical_cuts.extend([height, total_width - height])
horizontal_cuts = []
if v_boxes > 1:
compartment_length = (inner_length - (v_boxes - 1) * slot_width) / v_boxes
for i in range(1, v_boxes):
pos = height + (i * compartment_length) + (i * slot_width)
horizontal_cuts.append(pos)
horizontal_cuts.extend([height, total_length - height])
# --- Markdown Output ---
print("# Saw Operator Cut Plan")
print(f"## Board Dimensions: `{total_width} x {total_length} mm`")
print("")
print("## Specifications")
print(f"* **V-Groove Spec:** Depth `{groove_depth:.2f} mm`")
print(f"* **Slot Spec:** Width `{slot_width} mm`, Depth `{groove_depth:.2f} mm`")
print("")
print("## Blade Setup")
print("* **For V-Grooves:** Set saw blade angle to **45 degrees**.")
print("* **For Slots:** Set saw blade angle to **0 degrees** (straight up).")
print("")
# --- Setup 1: Vertical Cuts ---
print("## SETUP 1: VERTICAL CUTS")
print("### 1. Reference from LEFT edge:")
left_cuts = sorted([dist for dist in vertical_cuts if dist <= total_width / 2])
for dist in left_cuts:
cut_type = "V-GROOVE" if dist == height else "SLOT"
print(f"* Set fence to: `{dist:.2f} mm` -- for **{cut_type}**")
print("### 2. Reference from RIGHT edge (FLIP board 180 deg):")
right_cuts = sorted([total_width - dist for dist in vertical_cuts if dist > total_width / 2])
for dist in right_cuts:
abs_pos = total_width - dist
cut_type = "V-GROOVE" if abs_pos == (total_width - height) else "SLOT"
print(f"* Set fence to: `{dist:.2f} mm` -- for **{cut_type}**")
print("")
# --- Setup 2: Horizontal Cuts ---
print("## SETUP 2: HORIZONTAL CUTS")
print("### 1. Rotate board 90 degrees CCW, reference from NEW LEFT edge (original BOTTOM edge):")
bottom_cuts = sorted([dist for dist in horizontal_cuts if dist <= total_length / 2])
for dist in bottom_cuts:
cut_type = "V-GROOVE" if dist == height else "SLOT"
print(f"* Set fence to: `{dist:.2f} mm` -- for **{cut_type}**")
print("### 2. Reference from NEW RIGHT edge (original TOP edge, FLIP board 180 deg):")
top_cuts = sorted([total_length - dist for dist in horizontal_cuts if dist > total_length / 2])
for dist in top_cuts:
abs_pos = total_length - dist
cut_type = "V-GROOVE" if abs_pos == (total_length - height) else "SLOT"
print(f"* Set fence to: `{dist:.2f} mm` -- for **{cut_type}**")
print("")
print("---")
print("*End of Plan*")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate a cut plan for the poly-mech box.")
parser.add_argument('--total-width', type=float, default=500, help='Total width of the board')
parser.add_argument('--total-length', type=float, default=500, help='Total length of the board')
parser.add_argument('--height', type=float, default=80, help='Height of the box walls')
parser.add_argument('--base-thickness', type=float, default=3, help='Remaining thickness after V-groove')
parser.add_argument('--total-thickness', type=float, default=8, help='Total thickness of the material')
parser.add_argument('--slot-width', type=float, default=8, help='Width of the slots for internal dividers')
parser.add_argument('--u-boxes', type=int, default=2, help='Number of boxes along the width (U-axis)')
parser.add_argument('--v-boxes', type=int, default=2, help='Number of boxes along the length (V-axis)')
args = parser.parse_args()
generate_cut_plan(
args.total_width,
args.total_length,
args.height,
args.base_thickness,
args.total_thickness,
args.slot_width,
args.u_boxes,
args.v_boxes
)

View File

@ -0,0 +1,82 @@
# converter.py
# A Python script for use with FreeCAD's command-line interface.
# Converts a mesh file (like STL) into a solid model and exports it to STEP.
import sys
import FreeCAD
import Part
import Mesh
# --- Argument Validation ---
# The script name is sys.argv[0], followed by FreeCAD's internal args.
# The actual script arguments start after that. We expect two: input and output files.
# FreeCAD adds its own arguments, so we find our files at the end of the list.
if len(sys.argv) < 3:
print("Converter script usage: <input_file> <output_file>")
sys.exit(1)
input_file_path = sys.argv[-2]
output_file_path = sys.argv[-1]
print(f"Input file: {input_file_path}")
print(f"Output file: {output_file_path}")
# --- Conversion Logic ---
try:
# 1. Load the mesh from the input STL file
mesh = Mesh.read(input_file_path)
# 2. Split the mesh into its separate, unconnected components (shells)
components = mesh.getSeparateComponents()
print(f"Found {len(components)} separate components in the mesh.")
# 3. Convert each component mesh into a solid shape
solids = []
for i, component_mesh in enumerate(components):
try:
# Create a shape from the mesh component
shape = Part.Shape()
shape.makeShapeFromMesh(component_mesh.Topology, 0.1)
# Use a more robust method to ensure the shape is a solid
# This can fix minor issues and is more reliable than direct conversion.
if not shape.isNull() and shape.Faces:
solid = Part.Solid(Part.Shell(shape.Faces))
if solid.Volume > 0:
solids.append(solid)
else:
# If creating a solid fails, try refining it first.
refined_shape = shape.removeSplitter()
if not refined_shape.isNull() and refined_shape.Faces:
solid = Part.Solid(Part.Shell(refined_shape.Faces))
if solid.Volume > 0:
solids.append(solid)
else:
print(f"Warning: Component {i+1} could not be converted to a solid after refining.")
else:
print(f"Warning: Component {i+1} became null after refining.")
else:
print(f"Warning: Component {i+1} could not be converted to a shape.")
except Exception as ex:
print(f"Warning: An exception occurred while processing component {i+1}: {ex}")
if not solids:
print("Error: No valid solids could be created from the mesh.")
sys.exit(1)
# 4. Combine all solids into a single compound part for export
if len(solids) > 1:
result_shape = Part.Compound(solids)
else:
result_shape = solids[0]
# 5. Export the final shape to a STEP file
result_shape.exportStep(output_file_path)
print("STEP export completed successfully.")
sys.exit(0)
except Exception as e:
print(f"An error occurred during FreeCAD conversion: {e}")
sys.exit(1)

View File

@ -0,0 +1,41 @@
#!/bin/bash
# --- export_dxf.sh ---
# Exports a 2D projection of an OpenSCAD file to a DXF file.
#
# Usage: ./export_dxf.sh <source.scad> [output.dxf]
#
# Arguments:
# $1: source_file - Input .scad file (Required)
# $2: output_file - Output .dxf file (Optional)
# --- Input Validation ---
if [ -z "$1" ]; then
echo "Usage: $0 <source.scad> [output.dxf]"
echo "Error: Source file not specified."
exit 1
fi
# --- Argument Parsing ---
SOURCE_FILE="$1"
OUTPUT_FILE="$2"
# --- Set Default Output Filename ---
if [ -z "$OUTPUT_FILE" ]; then
OUTPUT_FILE="${SOURCE_FILE%.scad}_unfolded.dxf"
fi
# --- OpenSCAD DXF Export Command ---
echo "Exporting 2D projection from '$SOURCE_FILE' to '$OUTPUT_FILE'..."
openscad \
-o "$OUTPUT_FILE" \
-D "view_mode=\"dxf\"" \
"$SOURCE_FILE"
# --- Completion Message ---
if [ $? -eq 0 ]; then
echo "Export complete: '$OUTPUT_FILE' created successfully."
else
echo "Error: OpenSCAD DXF export failed."
exit 1
fi

View File

@ -0,0 +1,44 @@
#!/bin/bash
# --- export_freecad.sh ---
# Imports an OpenSCAD file into FreeCAD and saves it as a .FCStd project.
#
# !! REQUIRES FREECAD & OPENSCAD WORKBENCH !!
# This script depends on FreeCAD being installed, 'FreeCADCmd.exe'
# being in your system's PATH, and the OpenSCAD workbench being installed
# within FreeCAD.
#
# Usage: ./export_freecad.sh <source.scad> [output.FCStd]
#
# Arguments:
# $1: source_file - Input .scad file (Required)
# $2: output_file - Output .FCStd file (Optional)
# --- Input Validation ---
if [ -z "$1" ]; then
echo "Usage: $0 <source.scad> [output.FCStd]"
echo "Error: Source file not specified."
exit 1
fi
# --- Argument Parsing ---
SOURCE_FILE="$1"
OUTPUT_FILE="$2"
# --- Set Default Output Filename ---
if [ -z "$OUTPUT_FILE" ]; then
OUTPUT_FILE="${SOURCE_FILE%.scad}.FCStd"
fi
# --- Step 1: Import .scad and save as .FCStd using FreeCAD ---
echo "Importing '$SOURCE_FILE' and creating '$OUTPUT_FILE' using FreeCAD..."
# Note: Assuming FreeCADCmd.exe for Windows. Use 'freecadcmd' on Linux.
FreeCADCmd.exe -c save_fcstd.py "$SOURCE_FILE" "$OUTPUT_FILE"
if [ $? -ne 0 ]; then
echo "Error: FreeCAD project creation failed. Is FreeCAD and the OpenSCAD workbench installed and in your PATH?"
exit 1
fi
# --- Completion Message ---
echo "Export complete: '$OUTPUT_FILE' created successfully."

View File

@ -0,0 +1,66 @@
#!/bin/bash
# --- export_step.sh ---
# Converts an OpenSCAD file to a STEP file using FreeCAD.
#
# !! REQUIRES FREECAD !!
# This script depends on FreeCAD being installed and 'FreeCADCmd.exe'
# being available in your system's PATH.
#
# Usage: ./export_step.sh <source.scad> [output.step]
#
# Arguments:
# $1: source_file - Input .scad file (Required)
# $2: output_file - Output .step file (Optional)
# --- Input Validation ---
if [ -z "$1" ]; then
echo "Usage: $0 <source.scad> [output.step]"
echo "Error: Source file not specified."
exit 1
fi
# --- Argument Parsing ---
SOURCE_FILE="$1"
OUTPUT_FILE="$2"
# --- Set Default Output Filename ---
if [ -z "$OUTPUT_FILE" ]; then
OUTPUT_FILE="${SOURCE_FILE%.scad}.step"
fi
# --- Define Temporary STL file ---
TEMP_STL_FILE="temp_conversion.stl"
# --- Step 1: Export from OpenSCAD to STL ---
echo "Step 1/3: Exporting '$SOURCE_FILE' to STL using dedicated export layout..."
openscad \
-o "$TEMP_STL_FILE" \
-D "view_mode=\"export\"" \
"$SOURCE_FILE"
if [ $? -ne 0 ]; then
echo "Error: OpenSCAD export failed."
exit 1
fi
echo "STL export successful."
# --- Step 2: Convert STL to STEP using FreeCAD ---
echo "Step 2/3: Converting '$TEMP_STL_FILE' to '$OUTPUT_FILE' using FreeCAD..."
# Note: Assuming FreeCADCmd.exe for Windows. Use 'freecadcmd' on Linux.
FreeCADCmd.exe -c converter.py "$TEMP_STL_FILE" "$OUTPUT_FILE"
if [ $? -ne 0 ]; then
echo "Error: FreeCAD conversion failed. Is FreeCAD installed and in your PATH?"
rm -f "$TEMP_STL_FILE" # Clean up temp file
exit 1
fi
echo "STEP conversion successful."
# --- Step 3: Clean up temporary files ---
echo "Step 3/3: Cleaning up temporary files..."
rm -f "$TEMP_STL_FILE"
echo "Done."
# --- Completion Message ---
echo "Export complete: '$OUTPUT_FILE' created successfully."

View File

@ -0,0 +1,59 @@
#!/bin/bash
# --- export_stl.sh ---
# Exports an OpenSCAD file to an STL file.
#
# Usage: ./export_stl.sh <source.scad> [output.stl] [folded_state]
#
# Arguments:
# $1: source_file - Input .scad file (Required)
# $2: output_file - Output .stl file (Optional)
# $3: folded_state - 'true' or 'false'. (Optional, defaults to 'true' for a solid model)
# --- Input Validation ---
if [ -z "$1" ]; then
echo "Usage: $0 <source.scad> [output.stl] [folded_state]"
echo "Error: Source file not specified."
exit 1
fi
# --- Argument Parsing ---
SOURCE_FILE="$1"
OUTPUT_FILE=""
# Default to 'true' as we usually want to export the assembled solid model
FOLDED_STATE="true"
# Case 1: All three arguments are provided
if [ -n "$3" ]; then
OUTPUT_FILE="$2"
FOLDED_STATE="$3"
# Case 2: Only two arguments are provided
elif [ -n "$2" ]; then
# Check if the second argument is the folded state or a filename
if [[ "$2" == "true" || "$2" == "false" ]]; then
FOLDED_STATE="$2"
else
OUTPUT_FILE="$2"
fi
fi
# --- Set Default Output Filename ---
if [ -z "$OUTPUT_FILE" ]; then
OUTPUT_FILE="${SOURCE_FILE%.scad}_${FOLDED_STATE}.stl"
fi
# --- OpenSCAD Export Command ---
# -D: Overrides a variable in the OpenSCAD script.
echo "Exporting '$SOURCE_FILE' to '$OUTPUT_FILE' with Folded_View=$FOLDED_STATE..."
openscad \
-o "$OUTPUT_FILE" \
-D "Folded_View=$FOLDED_STATE" \
"$SOURCE_FILE"
# --- Completion Message ---
if [ $? -eq 0 ]; then
echo "Export complete: '$OUTPUT_FILE' created successfully."
else
echo "Error: OpenSCAD export failed."
exit 1
fi

BIN
products/poly-mech/cad/drawers/tools/folded_view.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,187 @@
# freecad_box_generator.py
#
# A Python script to generate a parametric box natively in FreeCAD.
#
# --- How to use (GUI) ---
# 1. Open FreeCAD and ensure you have a new, empty document open.
# 2. Paste this script into the Python console and press Enter.
#
# --- How to use (Command Line) ---
# FreeCADCmd.exe freecad_box_generator.py [output_file.FCStd]
import FreeCAD
import Part
import sys
# --- Customizable Parameters ---
# (Mirrors the parameters from the OpenSCAD file)
# Overall dimensions
TOTAL_WIDTH = 500
TOTAL_LENGTH = 500
BOX_HEIGHT = 80
# Material and slot properties
WALL_THICKNESS = 8
# Internal grid configuration
NUM_BOXES_U = 2 # Along X-axis (width)
NUM_BOXES_V = 2 # Along Y-axis (length)
def create_box_assembly(doc):
"""
Generates the box assembly as native FreeCAD objects in the specified document.
This function does not save the file.
"""
# --- Create a Group for organization ---
box_group = doc.addObject("App::DocumentObjectGroup", "BoxAssembly")
# --- Create the Base Plate ---
base = Part.makeBox(TOTAL_WIDTH, TOTAL_LENGTH, WALL_THICKNESS)
base_obj = doc.addObject("Part::Feature", "Base")
base_obj.Shape = base
box_group.addObject(base_obj)
# --- Calculate Inner Dimensions ---
inner_width = TOTAL_WIDTH - (2 * WALL_THICKNESS)
inner_length = TOTAL_LENGTH - (2 * WALL_THICKNESS)
# --- Create Outer Walls ---
# South Wall (along the front X-axis)
wall_south = Part.makeBox(TOTAL_WIDTH, WALL_THICKNESS, BOX_HEIGHT)
wall_south.translate(FreeCAD.Vector(0, 0, WALL_THICKNESS)) # Position it on top of the base
wall_south_obj = doc.addObject("Part::Feature", "Wall_South")
wall_south_obj.Shape = wall_south
box_group.addObject(wall_south_obj)
# North Wall (along the back X-axis)
wall_north = Part.makeBox(TOTAL_WIDTH, WALL_THICKNESS, BOX_HEIGHT)
wall_north.translate(FreeCAD.Vector(0, TOTAL_LENGTH - WALL_THICKNESS, WALL_THICKNESS))
wall_north_obj = doc.addObject("Part::Feature", "Wall_North")
wall_north_obj.Shape = wall_north
box_group.addObject(wall_north_obj)
# West Wall (along the left Y-axis, fits between N/S walls)
wall_west = Part.makeBox(WALL_THICKNESS, inner_length, BOX_HEIGHT)
wall_west.translate(FreeCAD.Vector(0, WALL_THICKNESS, WALL_THICKNESS))
wall_west_obj = doc.addObject("Part::Feature", "Wall_West")
wall_west_obj.Shape = wall_west
box_group.addObject(wall_west_obj)
# East Wall (along the right Y-axis, fits between N/S walls)
wall_east = Part.makeBox(WALL_THICKNESS, inner_length, BOX_HEIGHT)
wall_east.translate(FreeCAD.Vector(TOTAL_WIDTH - WALL_THICKNESS, WALL_THICKNESS, WALL_THICKNESS))
wall_east_obj = doc.addObject("Part::Feature", "Wall_East")
wall_east_obj.Shape = wall_east
box_group.addObject(wall_east_obj)
# --- Create Internal Dividers ---
if NUM_BOXES_U > 1:
# --- Vertical Dividers (along Y-axis) ---
compartment_width = inner_width / NUM_BOXES_U
for i in range(1, NUM_BOXES_U):
# Create the main body of the divider
x_pos = (i * compartment_width)
divider_v_body = Part.makeBox(WALL_THICKNESS, inner_length, BOX_HEIGHT)
# --- Create Notches for Horizontal Dividers (top-down) ---
if NUM_BOXES_V > 1:
# Create all notch cutting tools first
notch_tools = []
notch_cutout = Part.makeBox(WALL_THICKNESS, WALL_THICKNESS, BOX_HEIGHT / 2)
for j in range(1, NUM_BOXES_V):
compartment_length = inner_length / NUM_BOXES_V
y_pos = (j * compartment_length) - (WALL_THICKNESS / 2)
notch_tools.append(notch_cutout.translated(FreeCAD.Vector(0, y_pos, BOX_HEIGHT / 2)))
# Fuse them into a single cutting tool
cutting_compound = Part.makeCompound(notch_tools)
# Perform a single, efficient cut
divider_v_body = divider_v_body.cut(cutting_compound)
# Position the final divider and add to document
divider_v_body.translate(FreeCAD.Vector(x_pos, WALL_THICKNESS, WALL_THICKNESS))
divider_v_obj = doc.addObject("Part::Feature", f"Divider_V_{i}")
divider_v_obj.Shape = divider_v_body
box_group.addObject(divider_v_obj)
if NUM_BOXES_V > 1:
# --- Horizontal Dividers (along X-axis) ---
compartment_length = inner_length / NUM_BOXES_V
for i in range(1, NUM_BOXES_V):
# Create the main body of the divider
y_pos = (i * compartment_length)
divider_h_body = Part.makeBox(inner_width, WALL_THICKNESS, BOX_HEIGHT)
# --- Create Notches for Vertical Dividers (bottom-up) ---
if NUM_BOXES_U > 1:
# Create all notch cutting tools first
notch_tools = []
notch_cutout = Part.makeBox(WALL_THICKNESS, WALL_THICKNESS, BOX_HEIGHT / 2)
for j in range(1, NUM_BOXES_U):
compartment_width = inner_width / NUM_BOXES_U
x_pos = (j * compartment_width) - (WALL_THICKNESS / 2)
notch_tools.append(notch_cutout.translated(FreeCAD.Vector(x_pos, 0, 0)))
# Fuse them into a single cutting tool
cutting_compound = Part.makeCompound(notch_tools)
# Perform a single, efficient cut
divider_h_body = divider_h_body.cut(cutting_compound)
# Position the final divider and add to document
divider_h_body.translate(FreeCAD.Vector(WALL_THICKNESS, y_pos, WALL_THICKNESS))
divider_h_obj = doc.addObject("Part::Feature", f"Divider_H_{i}")
divider_h_obj.Shape = divider_h_body
box_group.addObject(divider_h_obj)
# --- Finalize ---
doc.recompute()
print("Box assembly generated.")
def main_cli():
"""Function to run when script is executed from the command line."""
output_file = "box.FCStd" # Default filename
# Look for the output file as a positional argument after the script name
# This is a robust way to handle arguments when FreeCAD's parser is unpredictable.
try:
script_index = [i for i, arg in enumerate(sys.argv) if 'freecad_box_generator.py' in arg][0]
if script_index + 1 < len(sys.argv):
output_file = sys.argv[script_index + 1]
except IndexError:
# This case handles running the script directly without FreeCAD, for debugging.
pass
doc = FreeCAD.newDocument("Box")
create_box_assembly(doc)
try:
doc.saveAs(output_file)
print(f"Successfully saved box assembly to: {output_file}")
except Exception as e:
print(f"Error saving file: {e}")
FreeCAD.closeDocument(doc.Name)
# Ensure the command-line application exits cleanly
sys.exit()
def main_gui():
"""Function to run when script is executed from the FreeCAD GUI."""
doc = FreeCAD.activeDocument()
if not doc:
doc = FreeCAD.newDocument("Box")
create_box_assembly(doc)
if FreeCAD.GuiUp:
FreeCAD.Gui.activeDocument().activeView().viewAxonometric()
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
# --- Main Execution Block ---
# This determines if the script is running in the GUI or from the command line
# and calls the appropriate main function.
if FreeCAD.GuiUp:
main_gui()
else:
main_cli()

View File

@ -0,0 +1,31 @@
#!/bin/bash
# --- run_freecad_generator.sh ---
#
# A wrapper script to correctly call the FreeCAD box generator from the command line.
#
# Usage: ./run_freecad_generator.sh <output_file.FCStd>
# --- Argument Validation ---
if [ -z "$1" ]; then
echo "Usage: $0 <output_file.FCStd>"
echo "Error: Output file not specified."
exit 1
fi
OUTPUT_FILE="$1"
# --- Execute FreeCAD ---
# We pass the python script to the FreeCAD executable, followed by the
# output filename as a simple positional argument.
echo "Executing FreeCAD generator script..."
FreeCADCmd.exe -c freecad_box_generator.py "$OUTPUT_FILE"
if [ $? -ne 0 ]; then
echo ""
echo "Error: FreeCAD script execution failed."
exit 1
fi
echo "Script executed successfully."

View File

@ -0,0 +1,41 @@
# save_fcstd.py
# A Python script for use with FreeCAD's command-line interface.
# Imports an OpenSCAD file and saves it as a FreeCAD project.
import sys
import FreeCAD
import Part
# --- Argument Validation ---
if len(sys.argv) < 3:
print("Converter script usage: <input_file.scad> <output_file.FCStd>")
sys.exit(1)
input_file_path = sys.argv[-2]
output_file_path = sys.argv[-1]
print(f"Input file: {input_file_path}")
print(f"Output file: {output_file_path}")
# --- Conversion Logic ---
try:
# 1. Create a new, empty FreeCAD document
doc = FreeCAD.newDocument("ImportedSCAD")
# 2. Use the Part module to import the .scad file into the document.
# This requires the OpenSCAD workbench to be installed in FreeCAD.
# FreeCAD manages the call to the 'openscad' executable itself.
Part.insert(input_file_path, doc.Name)
# 3. It's good practice to recompute the model after an import.
doc.recompute()
# 4. Save the document to the specified output file path.
doc.saveAs(output_file_path)
print("FreeCAD project file saved successfully.")
sys.exit(0)
except Exception as e:
print(f"An error occurred during FreeCAD project creation: {e}")
sys.exit(1)

View File

@ -0,0 +1,52 @@
#!/bin/bash
# --- generate_cut_plan.sh ---
# Generates a markdown cut plan from an OpenSCAD file.
# This script calls OpenSCAD in a special mode to echo the plan.
#
# Usage: ./generate_cut_plan.sh <source.scad> [output.md]
# If output file is not provided, prints to console.
# --- Input Validation ---
if [ -z "$1" ]; then
echo "Usage: $0 <source.scad> [output.md]"
echo "Error: Source file not specified."
exit 1
fi
SOURCE_FILE="$1"
OUTPUT_FILE="$2"
DUMMY_OUTPUT="cutplan_dummy.stl" # A dummy file to force command-line execution
# --- OpenSCAD Command ---
# We force a dummy output file with -o to prevent the GUI from launching.
# OpenSCAD prints echo() statements to stderr. We redirect stderr to stdout (2>&1),
# then use grep to filter for only the lines starting with "ECHO:",
# and then use sed to remove the prefix and surrounding quotes for a clean output.
CUTPLAN_CONTENT=$(openscad \
-o "$DUMMY_OUTPUT" \
-D "view_mode=\"cutplan\"" \
"$SOURCE_FILE" \
2>&1 | grep "ECHO:" | sed 's/ECHO: "//;s/"$//')
# Check the exit status of the openscad command.
if [ ${PIPESTATUS[0]} -ne 0 ]; then
echo ""
echo "Error: OpenSCAD command failed."
rm -f "$DUMMY_OUTPUT" # Clean up dummy file on failure
exit 1
fi
# Clean up the dummy file
rm -f "$DUMMY_OUTPUT"
# --- Output the result ---
if [ -z "$OUTPUT_FILE" ]; then
echo ""
echo "--- Generated Cut Plan (Markdown) ---"
echo "$CUTPLAN_CONTENT"
echo "-------------------------------------"
else
echo "$CUTPLAN_CONTENT" > "$OUTPUT_FILE"
echo "Cut plan successfully saved to '$OUTPUT_FILE'."
fi

View File

@ -0,0 +1,34 @@
#!/bin/bash
# --- generate_cutlist.sh ---
# Generates a markdown cut plan by calling an external Python script.
# This bypasses a bug in some OpenSCAD versions that prevents the echo
# module from functioning correctly.
#
# Usage: ./generate_cutlist.sh [output.md]
# All parameters are taken from the defaults in the python script.
# For custom dimensions, edit calculate_cuts.py or call it directly.
OUTPUT_FILE="$1"
# Run the python script to generate the markdown content.
# The script can be modified to change box parameters.
CUTLIST_CONTENT=$(python calculate_cuts.py)
# Check the exit status of the python script.
if [ $? -ne 0 ]; then
echo ""
echo "Error: Python script 'calculate_cuts.py' failed."
exit 1
fi
# --- Output the result ---
if [ -z "$OUTPUT_FILE" ]; then
echo ""
echo "--- Generated Cut Plan (Markdown) ---"
echo "$CUTLIST_CONTENT"
echo "-------------------------------------"
else
echo "$CUTLIST_CONTENT" > "$OUTPUT_FILE"
echo "Cut plan successfully saved to '$OUTPUT_FILE'."
fi

BIN
products/poly-mech/cad/drawers/tools/reference/500-10.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
products/poly-mech/cad/drawers/tools/reference/variables.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,64 @@
#!/bin/bash
# --- render.sh ---
# Renders an OpenSCAD file to a PNG image.
#
# Usage: ./render.sh <source_file.scad> [output_file.png] [folded_state]
#
# Arguments:
# $1: source_file - The path to the input OpenSCAD file. (Required)
# $2: output_file - The path for the output PNG image. (Optional)
# $3: folded_state - 'true' or 'false' to control the Folded_View parameter.
# (Optional, defaults to false)
# --- Input Validation ---
if [ -z "$1" ]; then
echo "Usage: $0 <source_file.scad> [output_file.png] [folded_state]"
echo "Error: Source file not specified."
exit 1
fi
# --- Argument Parsing ---
SOURCE_FILE="$1"
OUTPUT_FILE=""
FOLDED_STATE="false" # Default value
# Case 1: All three arguments are provided
if [ -n "$3" ]; then
OUTPUT_FILE="$2"
FOLDED_STATE="$3"
# Case 2: Only two arguments are provided
elif [ -n "$2" ]; then
# Check if the second argument is the folded state or a filename
if [[ "$2" == "true" || "$2" == "false" ]]; then
FOLDED_STATE="$2"
else
OUTPUT_FILE="$2"
fi
fi
# --- Set Default Output Filename ---
if [ -z "$OUTPUT_FILE" ]; then
# Removes the .scad extension and appends .png
OUTPUT_FILE="${SOURCE_FILE%.scad}_${FOLDED_STATE}.png"
fi
# --- OpenSCAD Render Command ---
# --view=iso: Sets the camera to a standard isometric view.
# --imgsize: Sets the output image resolution.
# -D: Overrides a variable in the OpenSCAD script.
echo "Rendering '$SOURCE_FILE' to '$OUTPUT_FILE' with Folded_View=$FOLDED_STATE..."
openscad \
-o "$OUTPUT_FILE" \
--view=iso \
--imgsize=1024,768 \
-D "Folded_View=$FOLDED_STATE" \
"$SOURCE_FILE"
# --- Completion Message ---
if [ $? -eq 0 ]; then
echo "Render complete: '$OUTPUT_FILE' created successfully."
else
echo "Error: OpenSCAD rendering failed."
exit 1
fi

View File

@ -0,0 +1,151 @@
using System;
using System.Runtime.InteropServices;
using CommandLine;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
namespace SolidWorksBoxGenerator
{
// Class to hold the parsed command-line options
public class Options
{
[Option('o', "output", Required = true, HelpText = "Absolute path for the output .SLDPRT file.")]
public string OutputFile { get; set; }
[Option('w', "width", Default = 500.0, HelpText = "Total width of the box.")]
public double Width { get; set; }
[Option('l', "length", Default = 500.0, HelpText = "Total length of the box.")]
public double Length { get; set; }
[Option('h', "height", Default = 80.0, HelpText = "Height of the box walls.")]
public double Height { get; set; }
[Option('t', "thickness", Default = 3.0, HelpText = "Sheet metal thickness.")]
public double Thickness { get; set; }
[Option('r', "radius", Default = 1.0, HelpText = "Bend radius for sheet metal.")]
public double Radius { get; set; }
}
class Program
{
static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed<Options>(o =>
{
try
{
Console.WriteLine("Attempting to connect to SolidWorks...");
SldWorks swApp = GetSldWorks();
if (swApp == null)
{
Console.WriteLine("Could not connect to SolidWorks. Please ensure it is running.");
return;
}
Console.WriteLine("Successfully connected to SolidWorks.");
GenerateBox(swApp, o);
}
catch (Exception e)
{
Console.WriteLine($"An error occurred: {e.Message}");
Console.WriteLine(e.StackTrace);
}
});
}
private static SldWorks GetSldWorks()
{
// Connect to a running instance of SolidWorks
try
{
return (SldWorks)Marshal.GetActiveObject("SldWorks.Application");
}
catch (COMException)
{
return null;
}
}
private static void GenerateBox(SldWorks swApp, Options opts)
{
// Convert all measurements from mm to meters for SolidWorks
double width = opts.Width / 1000.0;
double length = opts.Length / 1000.0;
double height = opts.Height / 1000.0;
double thickness = opts.Thickness / 1000.0;
double radius = opts.Radius / 1000.0;
// --- Step 1: Create a New Part Document ---
Console.WriteLine("Creating new part document...");
ModelDoc2 swModel = (ModelDoc2)swApp.NewPart();
if (swModel == null)
{
Console.WriteLine("Failed to create new part.");
return;
}
FeatureManager featMan = swModel.FeatureManager;
SketchManager skMan = swModel.SketchManager;
swModel.ClearSelection2(true);
// --- Step 2: Create the Base Flange ---
Console.WriteLine("Creating base flange...");
// Select the Top Plane (defined as "Plane3" internally by the API)
swModel.Extension.SelectByID2("Plane3", "PLANE", 0, 0, 0, false, 0, null, 0);
skMan.InsertSketch(true);
skMan.CreateCenterRectangle(0, 0, 0, width / 2, length / 2, 0);
skMan.InsertSketch(true);
Feature sheetMetalFeature = featMan.InsertSheetMetalBaseFlange2(thickness, false, radius, 0, 0, 0, 0.5, 0, 0, 0, false, false, false, false);
if(sheetMetalFeature == null)
{
Console.WriteLine("Failed to create base flange.");
swApp.CloseDoc(swModel.GetTitle());
return;
}
swModel.ClearSelection2(true);
// --- Step 3: Create Edge Flanges (Walls) ---
Console.WriteLine("Creating edge flanges for walls...");
object[] edges = new object[4];
edges[0] = GetEdgeByVertex(swModel, width / 2, length / 2, 0); // Front-Right edge
edges[1] = GetEdgeByVertex(swModel, -width / 2, length / 2, 0); // Front-Left edge
edges[2] = GetEdgeByVertex(swModel, -width / 2, -length / 2, 0); // Back-Left edge
edges[3] = GetEdgeByVertex(swModel, width / 2, -length / 2, 0); // Back-Right edge
// Selecting all 4 edges at once to create mitered corners automatically
swModel.Extension.SelectByID2(((IEntity)edges[0]).GetSelectionId(), "EDGE", 0,0,0, true, 1, null, 0);
swModel.Extension.SelectByID2(((IEntity)edges[1]).GetSelectionId(), "EDGE", 0,0,0, true, 1, null, 0);
swModel.Extension.SelectByID2(((IEntity)edges[2]).GetSelectionId(), "EDGE", 0,0,0, true, 1, null, 0);
swModel.Extension.SelectByID2(((IEntity)edges[3]).GetSelectionId(), "EDGE", 0,0,0, true, 1, null, 0);
featMan.InsertSheetMetalEdgeFlange(height, radius, (90.0 * Math.PI / 180.0), (int)swSheetMetalFlangeLengthMethod_e.swSheetMetalFlangeLengthMethod_OuterVirtualSharp, 0, (int)swSheetMetalFlangePosition_e.swSheetMetalFlangePosition_MaterialInside, false, 0, 0, 0, 0, false, false);
swModel.ClearSelection2(true);
// --- Step 4: Save and Close ---
Console.WriteLine($"Saving file to {opts.OutputFile}...");
bool saveSuccess = swModel.Extension.SaveAs(opts.OutputFile, (int)swSaveAsVersion_e.swSaveAsCurrentVersion, (int)swSaveAsOptions_e.swSaveAsOptions_Silent, null, 0, 0);
if (saveSuccess)
{
Console.WriteLine("File saved successfully.");
}
else
{
Console.WriteLine("Failed to save the file.");
}
swApp.CloseDoc(swModel.GetTitle());
}
// Helper function to find an edge based on one of its vertices' coordinates
private static IEdge GetEdgeByVertex(ModelDoc2 model, double x, double y, double z)
{
var body = ((PartDoc)model).GetBodies2((int)swBodyType_e.swSolidBody, true)[0] as IBody2;
var vertex = body.GetFinniestVertex(x,y,z) as IVertex;
return vertex.GetEdges()[0] as IEdge;
}
}
}

View File

@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
<LangVersion>latest</LangVersion>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<ItemGroup>
<!--
This is a reference to the main SolidWorks Interop Assembly.
You MUST update the HintPath to match the location of this file on your system.
It is typically in the SolidWorks installation directory.
-->
<Reference Include="SolidWorks.Interop.sldworks">
<HintPath>C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS\api\redist\SolidWorks.Interop.sldworks.dll</HintPath>
<EmbedInteropTypes>true</EmbedInteropTypes>
</Reference>
<!--
This is a reference to the SolidWorks constants Interop Assembly.
Update the HintPath to match the location of this file on your system.
-->
<Reference Include="SolidWorks.Interop.swconst">
<HintPath>C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS\api\redist\SolidWorks.Interop.swconst.dll</HintPath>
<EmbedInteropTypes>true</EmbedInteropTypes>
</Reference>
<!-- A reference for standard COM marshalling -->
<Reference Include="System"/>
</ItemGroup>
<ItemGroup>
<!-- This adds a popular library for parsing command-line arguments -->
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,100 @@
#!/usr/bin/env node
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const { spawn } = require('child_process');
const path = require('path');
// --- Command-Line Interface Definition ---
const argv = yargs(hideBin(process.argv))
.command(
'$0 [output]',
'Generate a parametric sheet metal box in SolidWorks',
(yargs) => {
return yargs
.positional('output', {
describe: 'Absolute path to the output .SLDPRT file. Defaults to a file in your Documents folder.',
default: path.join(require('os').homedir(), 'Documents', 'sw-generated-box.SLDPRT')
});
}
)
.option('width', {
alias: 'w',
type: 'number',
description: 'Total width of the box',
default: 500
})
.option('length', {
alias: 'l',
type: 'number',
description: 'Total length of the box',
default: 500
})
.option('height', {
alias: 'h',
type: 'number',
description: 'Height of the box walls',
default: 80
})
.option('thickness', {
alias: 't',
type: 'number',
description: 'Sheet metal thickness',
default: 3
})
.option('radius', {
alias: 'r',
type: 'number',
description: 'Bend radius for sheet metal',
default: 1
})
.help()
.argv;
// --- Constructing the Arguments for the C# Application ---
// Path to the C# executable. We assume it's in './sw/box/bin/Debug/net48/SolidWorksBoxGenerator.exe'
// You may need to adjust the '.net48' folder depending on your target framework version.
const generatorExePath = path.resolve(__dirname, '..', 'box', 'bin', 'Debug', 'net48', 'SolidWorksBoxGenerator.exe');
// The C# app expects arguments in the format "--key value"
const generatorArgs = [
'--output', argv.output,
'--width', argv.width,
'--length', argv.length,
'--height', argv.height,
'--thickness', argv.thickness,
'--radius', argv.radius
];
// --- Executing the C# Application ---
console.log('Executing SolidWorks Box Generator...');
console.log(`Command: "${generatorExePath}" ${generatorArgs.join(' ')}`);
try {
const generatorProcess = spawn(generatorExePath, generatorArgs);
generatorProcess.stdout.on('data', (data) => {
// Trim whitespace from generator output for cleaner logging
process.stdout.write(`[Generator]: ${data.toString().trim()}\n`);
});
generatorProcess.stderr.on('data', (data) => {
console.error(`[Generator ERROR]: ${data}`);
});
generatorProcess.on('close', (code) => {
console.log(`\nGenerator process exited with code ${code}`);
if (code !== 0) {
console.error("Box generation failed. Ensure SolidWorks is running.");
} else {
console.log(`Box generation complete. File saved to: ${argv.output}`);
}
});
} catch (error) {
console.error(`Failed to start the generator process. Make sure the C# project has been built.`);
console.error(`Expected executable at: ${generatorExePath}`);
console.error(error);
}

View File

@ -0,0 +1,196 @@
{
"name": "solidworks-box-generator-cli",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "solidworks-box-generator-cli",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"yargs": "^17.0.0"
},
"bin": {
"generate-box": "cli.js"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"license": "ISC",
"engines": {
"node": ">=12"
}
}
}
}

View File

@ -0,0 +1,18 @@
{
"name": "solidworks-box-generator-cli",
"version": "1.0.0",
"description": "A CLI for generating boxes in SolidWorks.",
"main": "cli.js",
"bin": {
"generate-box": "./cli.js"
},
"scripts": {
"start": "node cli.js",
"build": "echo 'Build step not required for this simple CLI. Linting or packaging could go here.'"
},
"dependencies": {
"yargs": "^17.0.0"
},
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,348 @@
// Customizable parameters for the box folding tool
// These values are based on the provided screenshot.
// Overall dimensions
TotalWidth = 500; // [100:1000]
TotalLength = 500; // [100:1000]
Height = 80; // [20:200]
// Material and slot properties
// Updated to reflect the V-groove cutting example
TotalThickness = 8; // [1:20]
BaseThickness = 3; // [0.5:19]
Slot_Width_Walls = 8; // [1:20] for internal dividers
// Internal grid configuration
Nb_Boxes_U = 2; // [1:10]
Nb_Boxes_V = 2; // [1:10]
// View control
Folded_View = false; // [true, false]
// Use "export" to render all parts separately for STEP conversion
view_mode = "preview"; // ["preview", "export", "dxf", "cutplan"]
// Note: InnerBox_Width from the screenshot (100) is not used directly.
// Instead, the compartment widths are calculated based on TotalWidth, Height, and Nb_Boxes_U/V.
// This ensures the design remains consistent with the overall dimensions.
// ---------------------------------------------------------------------
// Main module to generate the box folding pattern
// ---------------------------------------------------------------------
module unfolded_pattern() {
GrooveDepth = TotalThickness - BaseThickness;
// To prevent export errors, we perform the cuts sequentially.
// This is more stable than a single, complex difference().
difference() {
// Start with the result of the first cut...
difference() {
// 1. Create the base plate
cube([TotalWidth, TotalLength, TotalThickness], center = true);
// 2. Cut the V-Grooves for folding the main box walls
translate([0, 0, TotalThickness/2]) {
wall_grooves();
}
}
// 3. ...and then cut the internal rectangular slots from that result.
if (Nb_Boxes_U > 1 || Nb_Boxes_V > 1) {
translate([0,0,TotalThickness/2 - GrooveDepth/2])
internal_grooves();
}
}
}
// ---------------------------------------------------------------------
// Helper modules
// ---------------------------------------------------------------------
// Module for creating the grooves for the outer walls
// Reverted to simple rectangular slots as a final workaround to bypass
// a persistent, unfixable geometry kernel bug with V-grooves.
module wall_grooves() {
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
GrooveDepth = TotalThickness - BaseThickness;
// For a folding groove, the width should be slightly less than the material thickness
// to leave some material at the corners. We'll use the depth as a proxy.
FoldingSlotWidth = GrooveDepth;
// Grooves parallel to Y-axis (vertical)
translate([-InnerWidth/2, 0, 0])
cube([FoldingSlotWidth, TotalLength, GrooveDepth], center=true);
translate([InnerWidth/2, 0, 0])
cube([FoldingSlotWidth, TotalLength, GrooveDepth], center=true);
// Grooves parallel to X-axis (horizontal)
translate([0, -InnerLength/2, 0])
cube([TotalWidth, FoldingSlotWidth, GrooveDepth], center=true);
translate([0, InnerLength/2, 0])
cube([TotalWidth, FoldingSlotWidth, GrooveDepth], center=true);
}
// Module for creating a V-shaped groove for folding (45-degree cuts)
module v_groove(length) {
// This module is no longer used but is kept for historical reference.
}
// Module for creating the grooves for the internal compartments
module internal_grooves() {
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
GrooveDepth = TotalThickness - BaseThickness;
CompartmentWidth = (InnerWidth - (Nb_Boxes_U - 1) * Slot_Width_Walls) / Nb_Boxes_U;
CompartmentLength = (InnerLength - (Nb_Boxes_V - 1) * Slot_Width_Walls) / Nb_Boxes_V;
// Internal vertical grooves
if (Nb_Boxes_U > 1) {
for (i = [1 : Nb_Boxes_U - 1]) {
x_pos = -InnerWidth/2 + i * CompartmentWidth + (i - 1/2) * Slot_Width_Walls;
translate([x_pos, 0, 0])
cube([Slot_Width_Walls, InnerLength, GrooveDepth], center = true);
}
}
// Internal horizontal grooves
if (Nb_Boxes_V > 1) {
for (i = [1 : Nb_Boxes_V - 1]) {
y_pos = -InnerLength/2 + i * CompartmentLength + (i - 1/2) * Slot_Width_Walls;
translate([0, y_pos, 0])
cube([InnerWidth, Slot_Width_Walls, GrooveDepth], center = true);
}
}
}
// ---------------------------------------------------------------------
// Modules for Folded View
// ---------------------------------------------------------------------
// Generates the fully assembled 3D box
module folded_box() {
// Inner dimensions of the box
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
// Material thickness for all parts
wall_thickness = TotalThickness;
// 1. Base Plate
translate([0, 0, wall_thickness / 2])
cube([InnerWidth, InnerLength, wall_thickness], center = true);
// 2. Outer Walls
// South Wall (bottom) - Full Width
translate([0, -InnerLength/2, Height/2 + wall_thickness])
rotate([90, 0, 0])
cube([InnerWidth, Height, wall_thickness], center=true);
// North Wall (top) - Full Width
translate([0, InnerLength/2, Height/2 + wall_thickness])
rotate([-90, 0, 0])
cube([InnerWidth, Height, wall_thickness], center=true);
// West Wall (left) - Shortened to fit between North/South walls
translate([-InnerWidth/2, 0, Height/2 + wall_thickness])
rotate([0, 90, 0])
cube([Height, InnerLength - 2 * wall_thickness, wall_thickness], center=true);
// East Wall (right) - Shortened to fit between North/South walls
translate([InnerWidth/2, 0, Height/2 + wall_thickness])
rotate([0, -90, 0])
cube([Height, InnerLength - 2 * wall_thickness, wall_thickness], center=true);
// 3. Internal Dividers
internal_dividers_folded();
}
// Generates the interlocking internal dividers for the folded view
module internal_dividers_folded() {
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
divider_thickness = Slot_Width_Walls; // Use slot width as the divider material thickness
// Calculate compartment sizes
Compartment_U = (InnerWidth + divider_thickness) / Nb_Boxes_U;
Compartment_V = (InnerLength + divider_thickness) / Nb_Boxes_V;
// Vertical dividers (U-direction)
for (i = [1 : Nb_Boxes_U - 1]) {
x_pos = -InnerWidth/2 + i * Compartment_U - divider_thickness/2;
difference() {
// Main divider piece
translate([x_pos, 0, Height/2 + TotalThickness])
cube([divider_thickness, InnerLength, Height], center=true);
// Slots for horizontal dividers (top-down)
for (j = [1 : Nb_Boxes_V - 1]) {
y_pos = -InnerLength/2 + j * Compartment_V - divider_thickness/2;
translate([x_pos, y_pos, Height * 0.75 + TotalThickness])
cube([divider_thickness + 0.1, divider_thickness, Height/2], center=true);
}
}
}
// Horizontal dividers (V-direction)
for (j = [1 : Nb_Boxes_V - 1]) {
y_pos = -InnerLength/2 + j * Compartment_V - divider_thickness/2;
difference() {
// Main divider piece
translate([0, y_pos, Height/2 + TotalThickness])
cube([InnerWidth, divider_thickness, Height], center=true);
// Slots for vertical dividers (bottom-up)
for (i = [1 : Nb_Boxes_U - 1]) {
x_pos = -InnerWidth/2 + i * Compartment_U - divider_thickness/2;
translate([x_pos, y_pos, Height * 0.25 + TotalThickness])
cube([divider_thickness, divider_thickness + 0.1, Height/2], center=true);
}
}
}
}
// ---------------------------------------------------------------------
// Module for exporting all parts separately
// ---------------------------------------------------------------------
module export_layout() {
wall_thickness = TotalThickness;
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
// 1. Base Plate
translate([0, 0, -TotalThickness/2])
cube([InnerWidth, InnerLength, wall_thickness], center = true);
// Spacing for laying out parts
spacing = TotalWidth;
// 2. Outer Walls
translate([spacing, 0, 0])
cube([InnerWidth, Height, wall_thickness], center=true); // South
translate([spacing, Height + 10, 0])
cube([InnerWidth, Height, wall_thickness], center=true); // North
translate([spacing + InnerWidth + 10, 0, 0])
cube([InnerLength - 2 * wall_thickness, Height, wall_thickness], center=true); // West
translate([spacing + InnerWidth + 10, Height + 10, 0])
cube([InnerLength - 2 * wall_thickness, Height, wall_thickness], center=true); // East
// 3. Internal Dividers
divider_thickness = Slot_Width_Walls;
Compartment_U = (InnerWidth + divider_thickness) / Nb_Boxes_U;
Compartment_V = (InnerLength + divider_thickness) / Nb_Boxes_V;
// Place all vertical dividers in a row
for (i = [1 : Nb_Boxes_U - 1]) {
translate([2 * spacing + (i-1)*(divider_thickness+10), 0, 0])
internal_divider_vertical_export(InnerLength, Compartment_V);
}
// Place all horizontal dividers in a row
for (j = [1 : Nb_Boxes_V - 1]) {
translate([2 * spacing, (j-1)*(Height+10) + Height + 10, 0])
internal_divider_horizontal_export(InnerWidth, Compartment_U);
}
}
// Helper modules for export layout (without difference operations)
module internal_divider_vertical_export(length, compartment_v_size) {
divider_thickness = Slot_Width_Walls;
difference() {
cube([divider_thickness, length, Height], center=true);
// Notches for horizontal dividers (bottom-up)
for (i = [1 : Nb_Boxes_V - 1]) {
y_pos = -length/2 + i * compartment_v_size - divider_thickness/2;
translate([0, y_pos, -Height/4])
cube([divider_thickness + 0.1, divider_thickness, Height/2], center=true);
}
}
}
module internal_divider_horizontal_export(length, compartment_u_size) {
divider_thickness = Slot_Width_Walls;
difference() {
cube([length, divider_thickness, Height], center=true);
// Notches for vertical dividers (top-down)
for (j = [1 : Nb_Boxes_U - 1]) {
x_pos = -length/2 + j * compartment_u_size - divider_thickness/2;
translate([x_pos, 0, Height/4])
cube([divider_thickness, divider_thickness + 0.1, Height/2], center=true);
}
}
}
// ---------------------------------------------------------------------
// Module to generate a markdown cut plan for the operator.
// This version avoids all list manipulation for maximum compatibility.
// ---------------------------------------------------------------------
module generate_cut_plan_text() {
// --- Calculations ---
InnerWidth = TotalWidth - 2 * Height;
InnerLength = TotalLength - 2 * Height;
GrooveDepth = TotalThickness - BaseThickness;
// --- Markdown Output ---
echo("# Saw Operator Cut Plan");
echo(str("## Board Dimensions: `", TotalWidth, " x ", TotalLength, " mm`"));
echo("");
echo("## Specifications");
echo(str("* **V-Groove Spec:** Depth `", GrooveDepth, " mm`"));
echo(str("* **Slot Spec:** Width `", Slot_Width_Walls, " mm`, Depth `", GrooveDepth, " mm`"));
echo("");
echo("## Blade Setup");
echo("* **For V-Grooves:** Set saw blade angle to **45 degrees**.");
echo("* **For Slots:** Set saw blade angle to **0 degrees** (straight up).");
echo("");
// --- Setup 1: Vertical Cuts ---
echo("## SETUP 1: VERTICAL CUTS");
echo("All distances are from the LEFT edge. Flip board for cuts > 50%.");
echo("");
echo(str("* `", Height, " mm` -- **V-GROOVE**"));
if (Nb_Boxes_U > 1) {
for (i = [1 : Nb_Boxes_U - 1]) {
pos = Height + i * ((InnerWidth - (Nb_Boxes_U - 1) * Slot_Width_Walls) / Nb_Boxes_U) + i * Slot_Width_Walls;
echo(str("* `", pos, " mm` -- **SLOT**"));
}
}
echo(str("* `", TotalWidth - Height, " mm` -- **V-GROOVE**"));
echo("");
// --- Setup 2: Horizontal Cuts ---
echo("## SETUP 2: HORIZONTAL CUTS");
echo("Rotate board 90 deg. All distances from the NEW LEFT edge.");
echo("");
echo(str("* `", Height, " mm` -- **V-GROOVE**"));
if (Nb_Boxes_V > 1) {
for (i = [1 : Nb_Boxes_V - 1]) {
pos = Height + i * ((InnerLength - (Nb_Boxes_V - 1) * Slot_Width_Walls) / Nb_Boxes_V) + i * Slot_Width_Walls;
echo(str("* `", pos, " mm` -- **SLOT**"));
}
}
echo(str("* `", TotalLength - Height, " mm` -- **V-GROOVE**"));
echo("");
echo("---");
echo("*End of Plan*");
// Generate a tiny invisible cube because OpenSCAD needs to produce some geometry.
cube(0.01);
}
// ---------------------------------------------------------------------
// Render the final object
// ---------------------------------------------------------------------
if (view_mode == "export") {
export_layout();
} else if (view_mode == "dxf") {
// Project the 2D unfolded pattern for DXF export
projection(cut = true) unfolded_pattern();
} else if (view_mode == "cutplan") {
generate_cut_plan_text();
} else {
if (Folded_View) {
folded_box();
} else {
unfolded_pattern();
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,72 @@
<span id="specs" style="padding: 16px">
<table class="center specs">
<tbody>
<tr>
<td>Type
</td>
<td>Sheetpress
</td>
</tr>
<tr>
<td>Version
</td>
<td>0.1
</td>
</tr>
<tr>
<td>Weight
</td>
<td>80 - 120 Kg
</td>
</tr>
<tr>
<td> Voltage
</td>
<td>380V
</td>
</tr>
<tr>
<td> Status
</td>
<td>Development
</td>
</tr>
<tr>
<td> Sheet Size
</td>
<td>60 x 60 cm
</td>
</tr>
<tr>
<td> Plate Size
</td>
<td>70 x 70 cm
</td>
</tr>
<tr>
<td> Pressure
</td>
<td> 2-5 tons, depending on the sheet thickness
</td>
</tr>
<tr>
<td> Input Flake Size
</td>
<td>Medium, Small
</td>
</tr>
<tr>
<td> Gearbox ratio
</td>
<td> 1:15
</td>
</tr>
<tr>
<td>Heaters
</td>
<td> 300 Watt x 32 (10kW)
</td>
</tr>
</tbody>
</table>
</span>

View File

@ -0,0 +1,6 @@
**Product Resources**
- [3D Preview](${abs_url}/${product_rel}resources/edrawings.html)
- [Product Page](${product_page})
${product_howtos}

View File

@ -0,0 +1 @@
### Build the machine

View File

@ -0,0 +1 @@
${social}

View File

@ -0,0 +1 @@
### Media

View File

@ -0,0 +1,30 @@
<span id="specs" style="padding: 16px">
<table class="center specs">
<tbody>
<tr>
<td>Type:
</td>
<td>Sheetpress
</td>
</tr>
<tr>
<td>Version:
</td>
<td>0.1
</td>
</tr>
<tr>
<td> Status:
</td>
<td> Development
</td>
</tr>
<tr>
<td> Authors </td>
<td>
<li>[PlasticHub S.L.](${author_link})</li>
</td>
</tr>
</tbody>
</table>
</span>

View File

@ -0,0 +1,2 @@
- [3D Preview](${product_3d})
- [Electrical wiring](${product_wiring})

View File

@ -0,0 +1 @@
### Resources

View File

@ -0,0 +1,10 @@
Flexible, modular dual sheetpress, including a heat and cooling press.
## Details / Features
- Sheet thickness up to 30mm
- 15T of pressure
- Stackable and interlockable (600x1300cm sheets)
- cartridge heaters are embedded in the heat plates, enabling 30% better heat efficiency
- Press plate options: heating, passive or active cooling
- the hydraulic jack has a pneumatic port, to use a compressor instead

View File

@ -0,0 +1,4 @@
[%- i18n %]
- Sheet thickness up to 30mm
- 15T of pressure
[%- i18n-end %]

View File

@ -0,0 +1 @@
### Build the machine

View File

@ -0,0 +1,37 @@
## Force Calculations
to demonstrate that the motor and gearbox are capable of at least 2 tons clamping force, lifting a platten that weighs 20kg
Lead screw calculation:
```math
Torque(raise) = F x Dm/2x(L+u x PI x DM)/(PI x Dm-u x L)
```
[See leadscrew force torque calculator](https://daycounter.com/Calculators/Lead-Screw-Force-Torque-Calculator.phtml)
Toggle clamp calculations:
```math
P = F a / 2 h
```
where P = input force, F = output force, a = arm length, h = height (displacement of toggle pivot from line between end pivots)
[See toggle joint calculation](https://www.engineeringtoolbox.com/toggle-joint-d_2077.html)
Torque of 25Nm from the gearbox (as claimed) on a 20mm screw with a 4mm pitch and a brass nut with a frictional coefficient of 0.06 gives about 1575N pushing force on the nut.
Connected to an equal toggle mechanism of 125mm arm lengths, this would generate a perpendicular force of 820 newtons (80Kg) at the bottom of the stroke (toggle displacement 120mm), 98438 newtons (10 tons) at the top of the stroke (toggle displacement 1mm).
With a toggle diplacement of 5mm (press almost fully closed) the force generated is 19688 newtons or around 2 tons.
18nm from the gearbox (1.2NM x 15:1) gives 1135N push on the screw, generating 70938N (7.2T) at 1mm toggle displacement and 14118N (1.4T)at 5mm
Using a higher frictional coefficient of 0.1, torque of 18Nm from the gearbox gives 690N push on the screw, generating 359N (40Kg) at 120mm displacement 8628N (900Kg) at 5mm and 43125 (4.4T) at 1mm displacement. This seems like a more realistic estimate, given that friction on the collar and toggle pivot joints is not accounted for in this equation. Even half this would give a comfortable 2 ton max clamping force and still be able to lift 20kg from fully open. Also these calculations are based on the smallest stepper in the Nema 23 package size, up to 3nm are available
More detailed calculations will be performed using the [calculator](https://cdn.automationdirect.com/static/manuals/surestepmanual/appxc.pdf)
At max motor speed of 400 RRM with the 15:1 gearbox and 4mm pitch screw, the motor would move the nut on the screw at 1.7mm/s, meaning the plattens would take about 70s to perform a full 120mm stroke. If a 10:1 gearbox were used, it would take 45s.

View File

@ -0,0 +1 @@
${social}

View File

@ -0,0 +1,35 @@
---
title: "Cassandra v1.0 - revision-A DataSheet"
permalink: "/specs/products/cassandra"
layout: "print"
---
## {{page.title}}
### Specification
| Specification | |
|----------|-------------|
| Title | Cassandra |
| Version | v1.0 |
| Type | Sheetpress |
| Weight | 100 kg |
| Package Dimension | 1052 x 1100 x 900 mm |
| Power (W) | 3 kW |
| Voltage | 380V or 220V |
| Amperage | 16A |
| Input Flake Size | Small |
| Screw diameter | 30mm |
| Rated Motor Power | 3 kW |
| Motor Type | |
<div class="col">
<h3 class="text-center" id="dimensions">Dimensions</h3>
<div class="vertical-margin vertical-gap">
{% include product_image.html size="medium" src="/products/cassandra/drawings/dimensions.jpg" target="/products/cassandra/drawings/dimensions.PDF" %}
</div>
</div>
### Components & Parts
{% include product_image.html size="small" src="/products/cassandra/drawings/parts.jpg" %}

View File

@ -0,0 +1,2 @@
- [3D Preview](${product_3d})
- [Electrical wiring](${product_wiring})

View File

@ -0,0 +1 @@
### Resources

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,72 @@
<span id="specs" style="padding: 16px">
<table class="center specs">
<tbody>
<tr>
<td>Type
</td>
<td>Sheetpress
</td>
</tr>
<tr>
<td>Version
</td>
<td>0.1
</td>
</tr>
<tr>
<td>Weight
</td>
<td>80 - 120 Kg
</td>
</tr>
<tr>
<td> Voltage
</td>
<td>380V
</td>
</tr>
<tr>
<td> Status
</td>
<td>Development
</td>
</tr>
<tr>
<td> Sheet Size
</td>
<td>60 x 60 cm
</td>
</tr>
<tr>
<td> Plate Size
</td>
<td>70 x 70 cm
</td>
</tr>
<tr>
<td> Pressure
</td>
<td> 2-5 tons, depending on the sheet thickness
</td>
</tr>
<tr>
<td> Input Flake Size
</td>
<td>Medium, Small
</td>
</tr>
<tr>
<td> Gearbox ratio
</td>
<td> 1:15
</td>
</tr>
<tr>
<td>Heaters
</td>
<td> 300 Watt x 32 (10kW)
</td>
</tr>
</tbody>
</table>
</span>

View File

@ -0,0 +1,6 @@
**Product Resources**
- [3D Preview](${abs_url}/${product_rel}resources/edrawings.html)
- [Product Page](${product_page})
${product_howtos}

View File

@ -0,0 +1 @@
### Build the machine

View File

@ -0,0 +1 @@
${social}

View File

@ -0,0 +1 @@
### Media

View File

@ -0,0 +1,30 @@
<span id="specs" style="padding: 16px">
<table class="center specs">
<tbody>
<tr>
<td>Type:
</td>
<td>Sheetpress
</td>
</tr>
<tr>
<td>Version:
</td>
<td>0.1
</td>
</tr>
<tr>
<td> Status:
</td>
<td> Development
</td>
</tr>
<tr>
<td> Authors </td>
<td>
<li>[PlasticHub S.L.](${author_link})</li>
</td>
</tr>
</tbody>
</table>
</span>

View File

@ -0,0 +1,2 @@
- [3D Preview](${product_3d})
- [Electrical wiring](${product_wiring})

View File

@ -0,0 +1 @@
### Resources

View File

@ -0,0 +1,10 @@
Flexible, modular dual sheetpress, including a heat and cooling press.
## Details / Features
- Sheet thickness up to 30mm
- 15T of pressure
- Stackable and interlockable (600x1300cm sheets)
- cartridge heaters are embedded in the heat plates, enabling 30% better heat efficiency
- Press plate options: heating, passive or active cooling
- the hydraulic jack has a pneumatic port, to use a compressor instead

View File

@ -0,0 +1,4 @@
[%- i18n %]
- Sheet thickness up to 30mm
- 15T of pressure
[%- i18n-end %]

View File

@ -0,0 +1 @@
### Build the machine

Some files were not shown because too many files have changed in this diff Show More