saw cut list gen
This commit is contained in:
parent
64f676a716
commit
76c3a3cdff
@ -7,9 +7,10 @@ TotalLength = 500; // [100:1000]
|
||||
Height = 80; // [20:200]
|
||||
|
||||
// Material and slot properties
|
||||
TotalThickness = 5; // [1:20]
|
||||
// Updated to reflect the V-groove cutting example
|
||||
TotalThickness = 8; // [1:20]
|
||||
BaseThickness = 3; // [0.5:19]
|
||||
Slot_Width_Walls = 8; // [1:20]
|
||||
Slot_Width_Walls = 8; // [1:20] for internal dividers
|
||||
|
||||
// Internal grid configuration
|
||||
Nb_Boxes_U = 2; // [1:10]
|
||||
@ -18,7 +19,7 @@ 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", "cutlist"]
|
||||
view_mode = "preview"; // ["preview", "export", "dxf"]
|
||||
|
||||
// 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.
|
||||
@ -30,20 +31,25 @@ view_mode = "preview"; // ["preview", "export", "dxf", "cutlist"]
|
||||
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() {
|
||||
// 1. Create the base plate
|
||||
cube([TotalWidth, TotalLength, TotalThickness], center = true);
|
||||
// Start with the result of the first cut...
|
||||
difference() {
|
||||
// 1. Create the base plate
|
||||
cube([TotalWidth, TotalLength, TotalThickness], center = true);
|
||||
|
||||
// 2. Create the cutting grooves
|
||||
translate([0, 0, TotalThickness/2 - GrooveDepth/2]) {
|
||||
// Grooves for the main box walls
|
||||
wall_grooves();
|
||||
|
||||
// Grooves for internal compartments
|
||||
if (Nb_Boxes_U > 1 || Nb_Boxes_V > 1) {
|
||||
internal_grooves();
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,22 +58,33 @@ module unfolded_pattern() {
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// 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([Slot_Width_Walls, TotalLength, GrooveDepth], center=true);
|
||||
cube([FoldingSlotWidth, TotalLength, GrooveDepth], center=true);
|
||||
translate([InnerWidth/2, 0, 0])
|
||||
cube([Slot_Width_Walls, TotalLength, GrooveDepth], center=true);
|
||||
cube([FoldingSlotWidth, TotalLength, GrooveDepth], center=true);
|
||||
|
||||
// Grooves parallel to X-axis (horizontal)
|
||||
translate([0, -InnerLength/2, 0])
|
||||
cube([TotalWidth, Slot_Width_Walls, GrooveDepth], center=true);
|
||||
cube([TotalWidth, FoldingSlotWidth, GrooveDepth], center=true);
|
||||
translate([0, InnerLength/2, 0])
|
||||
cube([TotalWidth, Slot_Width_Walls, GrooveDepth], center=true);
|
||||
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
|
||||
@ -254,60 +271,6 @@ module internal_divider_horizontal_export(length, compartment_u_size) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Module to generate a cut list for the operator
|
||||
// ---------------------------------------------------------------------
|
||||
module generate_cutlist() {
|
||||
// --- Calculations ---
|
||||
InnerWidth = TotalWidth - 2 * Height;
|
||||
InnerLength = TotalLength - 2 * Height;
|
||||
|
||||
// --- Header ---
|
||||
echo("--- Cut List for Saw Operator ---");
|
||||
echo(str("Board Dimensions: ", TotalWidth, " x ", TotalLength, " mm"));
|
||||
echo("All distances are for the centerline of the slots.");
|
||||
echo("---");
|
||||
|
||||
// --- Vertical Cuts (Parallel to Y-axis) ---
|
||||
echo("VERTICAL CUTS (Distances from left edge):");
|
||||
// Outer walls
|
||||
v_outer_1 = Height;
|
||||
v_outer_2 = TotalWidth - Height;
|
||||
echo(str(" Outer Wall 1: ", v_outer_1, " mm"));
|
||||
echo(str(" Outer Wall 2: ", v_outer_2, " mm"));
|
||||
|
||||
// Internal dividers
|
||||
if (Nb_Boxes_U > 1) {
|
||||
CompartmentWidth = (InnerWidth - (Nb_Boxes_U - 1) * Slot_Width_Walls) / Nb_Boxes_U;
|
||||
for (i = [1 : Nb_Boxes_U - 1]) {
|
||||
x_pos_abs = Height + i * CompartmentWidth + i * Slot_Width_Walls;
|
||||
echo(str(" Internal Divider ", i, ": ", x_pos_abs, " mm"));
|
||||
}
|
||||
}
|
||||
echo("---");
|
||||
|
||||
// --- Horizontal Cuts (Parallel to X-axis) ---
|
||||
echo("HORIZONTAL CUTS (Distances from bottom edge):");
|
||||
// Outer walls
|
||||
h_outer_1 = Height;
|
||||
h_outer_2 = TotalLength - Height;
|
||||
echo(str(" Outer Wall 1: ", h_outer_1, " mm"));
|
||||
echo(str(" Outer Wall 2: ", h_outer_2, " mm"));
|
||||
|
||||
// Internal dividers
|
||||
if (Nb_Boxes_V > 1) {
|
||||
CompartmentLength = (InnerLength - (Nb_Boxes_V - 1) * Slot_Width_Walls) / Nb_Boxes_V;
|
||||
for (i = [1 : Nb_Boxes_V - 1]) {
|
||||
y_pos_abs = Height + i * CompartmentLength + i * Slot_Width_Walls;
|
||||
echo(str(" Internal Divider ", i, ": ", y_pos_abs, " mm"));
|
||||
}
|
||||
}
|
||||
echo("--- End of List ---");
|
||||
|
||||
// Generate a tiny invisible cube because OpenSCAD needs to produce some geometry.
|
||||
cube(0.01);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Render the final object
|
||||
// ---------------------------------------------------------------------
|
||||
@ -316,8 +279,6 @@ if (view_mode == "export") {
|
||||
} else if (view_mode == "dxf") {
|
||||
// Project the 2D unfolded pattern for DXF export
|
||||
projection(cut = true) unfolded_pattern();
|
||||
} else if (view_mode == "cutlist") {
|
||||
generate_cutlist();
|
||||
} else {
|
||||
if (Folded_View) {
|
||||
folded_box();
|
||||
|
||||
BIN
cad/drawers/tools/box_folding_tool_false.png
(Stored with Git LFS)
Normal file
BIN
cad/drawers/tools/box_folding_tool_false.png
(Stored with Git LFS)
Normal file
Binary file not shown.
1542
cad/drawers/tools/box_folding_tool_false.stl
Normal file
1542
cad/drawers/tools/box_folding_tool_false.stl
Normal file
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,150 @@ LINE
|
||||
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
|
||||
|
||||
101
cad/drawers/tools/calculate_cuts.py
Normal file
101
cad/drawers/tools/calculate_cuts.py
Normal 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
|
||||
)
|
||||
25
cad/drawers/tools/cl.md
Normal file
25
cad/drawers/tools/cl.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 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*
|
||||
21
cad/drawers/tools/cutlist.md
Normal file
21
cad/drawers/tools/cutlist.md
Normal file
@ -0,0 +1,21 @@
|
||||
# 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*
|
||||
@ -1,14 +0,0 @@
|
||||
--- Cut List for Saw Operator ---
|
||||
Board Dimensions: 500 x 500 mm
|
||||
All distances are for the centerline of the slots.
|
||||
---
|
||||
VERTICAL CUTS (Distances from left edge):
|
||||
Outer Wall 1: 80 mm
|
||||
Outer Wall 2: 420 mm
|
||||
Internal Divider 1: 254 mm
|
||||
---
|
||||
HORIZONTAL CUTS (Distances from bottom edge):
|
||||
Outer Wall 1: 80 mm
|
||||
Outer Wall 2: 420 mm
|
||||
Internal Divider 1: 254 mm
|
||||
--- End of List ---
|
||||
@ -1,51 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
# --- generate_cutlist.sh ---
|
||||
# Generates a cut list of slot distances from an OpenSCAD file.
|
||||
# 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 <source.scad> [output.txt]
|
||||
# If output file is not provided, prints to console.
|
||||
# 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.
|
||||
|
||||
# --- Input Validation ---
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <source.scad> [output.txt]"
|
||||
echo "Error: Source file not specified."
|
||||
exit 1
|
||||
fi
|
||||
OUTPUT_FILE="$1"
|
||||
|
||||
SOURCE_FILE="$1"
|
||||
OUTPUT_FILE="$2"
|
||||
DUMMY_OUTPUT="cutlist_dummy.stl" # A dummy file to force command-line execution
|
||||
# Run the python script to generate the markdown content.
|
||||
# The script can be modified to change box parameters.
|
||||
CUTLIST_CONTENT=$(python calculate_cuts.py)
|
||||
|
||||
# --- 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.
|
||||
CUTLIST_CONTENT=$(openscad \
|
||||
-o "$DUMMY_OUTPUT" \
|
||||
-D "view_mode=\"cutlist\"" \
|
||||
"$SOURCE_FILE" \
|
||||
2>&1 | grep "ECHO:" | sed 's/ECHO: "//;s/"$//')
|
||||
|
||||
# Check the exit status of the openscad command.
|
||||
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
||||
# Check the exit status of the python script.
|
||||
if [ $? -ne 0 ]; then
|
||||
echo ""
|
||||
echo "Error: OpenSCAD command failed."
|
||||
rm -f "$DUMMY_OUTPUT" # Clean up dummy file on failure
|
||||
echo "Error: Python script 'calculate_cuts.py' failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up the dummy file
|
||||
rm -f "$DUMMY_OUTPUT"
|
||||
|
||||
# --- Output the result ---
|
||||
if [ -z "$OUTPUT_FILE" ]; then
|
||||
echo ""
|
||||
echo "--- Generated Cut List ---"
|
||||
echo "--- Generated Cut Plan (Markdown) ---"
|
||||
echo "$CUTLIST_CONTENT"
|
||||
echo "--------------------------"
|
||||
echo "-------------------------------------"
|
||||
else
|
||||
echo "$CUTLIST_CONTENT" > "$OUTPUT_FILE"
|
||||
echo "Cut list successfully saved to '$OUTPUT_FILE'."
|
||||
echo "Cut plan successfully saved to '$OUTPUT_FILE'."
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user