freecad fuckery :)
This commit is contained in:
parent
d3c45bff2f
commit
8a7be7a68d
Binary file not shown.
Binary file not shown.
@ -1,25 +0,0 @@
|
||||
# Saw Operator Cut Plan
|
||||
## Board Dimensions: `500 x 500 mm`
|
||||
|
||||
## Specifications
|
||||
* **V-Groove Spec:** Depth `5 mm`
|
||||
* **Slot Spec:** Width `8 mm`, Depth `5 mm`
|
||||
|
||||
## Blade Setup
|
||||
* **For V-Grooves:** Set saw blade angle to **45 degrees**.
|
||||
* **For Slots:** Set saw blade angle to **0 degrees** (straight up).
|
||||
|
||||
## SETUP 1: VERTICAL CUTS
|
||||
### 1. Reference from LEFT edge:
|
||||
* Set fence to: `80 mm` -- for **V-GROOVE**
|
||||
### 2. Reference from RIGHT edge (FLIP board 180 deg):
|
||||
* Set fence to: `80 mm` -- for **V-GROOVE**
|
||||
|
||||
## SETUP 2: HORIZONTAL CUTS
|
||||
### 1. Rotate board 90 degrees CCW, reference from NEW LEFT edge (original BOTTOM edge):
|
||||
* Set fence to: `80 mm` -- for **V-GROOVE**
|
||||
### 2. Reference from NEW RIGHT edge (original TOP edge, FLIP board 180 deg):
|
||||
* Set fence to: `80 mm` -- for **V-GROOVE**
|
||||
|
||||
---
|
||||
*End of Plan*
|
||||
@ -1,27 +0,0 @@
|
||||
# Saw Operator Cut Plan
|
||||
## Board Dimensions: `500 x 500 mm`
|
||||
|
||||
## Specifications
|
||||
* **V-Groove Spec:** Depth `5 mm`
|
||||
* **Slot Spec:** Width `8 mm`, Depth `5 mm`
|
||||
|
||||
## Blade Setup
|
||||
* **For V-Grooves:** Set saw blade angle to **45 degrees**.
|
||||
* **For Slots:** Set saw blade angle to **0 degrees** (straight up).
|
||||
|
||||
## SETUP 1: VERTICAL CUTS
|
||||
All distances are from the LEFT edge. Flip board for cuts > 50%.
|
||||
|
||||
* `80 mm` -- **V-GROOVE**
|
||||
* `254 mm` -- **SLOT**
|
||||
* `420 mm` -- **V-GROOVE**
|
||||
|
||||
## SETUP 2: HORIZONTAL CUTS
|
||||
Rotate board 90 deg. All distances from the NEW LEFT edge.
|
||||
|
||||
* `80 mm` -- **V-GROOVE**
|
||||
* `254 mm` -- **SLOT**
|
||||
* `420 mm` -- **V-GROOVE**
|
||||
|
||||
---
|
||||
*End of Plan*
|
||||
@ -1,21 +0,0 @@
|
||||
# Saw Operator Cut Plan
|
||||
## Board Dimensions: `500 x 500 mm`
|
||||
|
||||
## Specifications
|
||||
* **V-Groove Spec:** Depth `5 mm`
|
||||
* **Slot Spec:** Width `8 mm`, Depth `5 mm`
|
||||
|
||||
## Blade Setup
|
||||
* **For V-Grooves:** Set saw blade angle to **45 degrees**.
|
||||
* **For Slots:** Set saw blade angle to **0 degrees** (straight up).
|
||||
|
||||
## SETUP 1: VERTICAL CUTS
|
||||
### 1. Reference from LEFT edge:
|
||||
### 2. Reference from RIGHT edge (FLIP board 180 deg):
|
||||
|
||||
## SETUP 2: HORIZONTAL CUTS
|
||||
### 1. Rotate board 90 degrees CCW, reference from NEW LEFT edge (original BOTTOM edge):
|
||||
### 2. Reference from NEW RIGHT edge (original TOP edge, FLIP board 180 deg):
|
||||
|
||||
---
|
||||
*End of Plan*
|
||||
187
cad/drawers/tools/freecad_box_generator.py
Normal file
187
cad/drawers/tools/freecad_box_generator.py
Normal 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()
|
||||
BIN
cad/drawers/tools/my_final_box.FCStd
Normal file
BIN
cad/drawers/tools/my_final_box.FCStd
Normal file
Binary file not shown.
31
cad/drawers/tools/run_freecad_generator.sh
Normal file
31
cad/drawers/tools/run_freecad_generator.sh
Normal 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."
|
||||
348
cad/drawers/tools/test/box_folding_tool.scad
Normal file
348
cad/drawers/tools/test/box_folding_tool.scad
Normal 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();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user