348 lines
14 KiB
OpenSCAD
348 lines
14 KiB
OpenSCAD
// 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();
|
|
}
|
|
} |