seedling tray: add water tray and pressing tool
This commit is contained in:
+43
-14
@@ -20,28 +20,30 @@ def render_scad(
|
||||
rows: int,
|
||||
cell_size: float,
|
||||
tray_height: float,
|
||||
water_tray_height: float,
|
||||
render_mode: str,
|
||||
) -> None:
|
||||
cmd = (
|
||||
f'openscad '
|
||||
f'-D cols={cols} '
|
||||
f'-D rows={rows} '
|
||||
f'-D cell_size={cell_size} '
|
||||
f'-D tray_height={tray_height} '
|
||||
f'-D \'render_mode="{render_mode}"\' '
|
||||
f'-o "{out_file}" '
|
||||
f'"{scad_file}"'
|
||||
)
|
||||
cmd = [
|
||||
"openscad",
|
||||
"-D", f"cols={cols}",
|
||||
"-D", f"rows={rows}",
|
||||
"-D", f"cell_size={cell_size}",
|
||||
"-D", f"tray_height={tray_height}",
|
||||
"-D", f"water_tray_height={water_tray_height}",
|
||||
"-D", f'render_mode="{render_mode}"',
|
||||
"-o", str(out_file),
|
||||
str(scad_file),
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
["bash", "-lc", cmd],
|
||||
cmd,
|
||||
cwd=BASE_DIR,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"\nFailed to render {scad_file.name}")
|
||||
print(f"\nFailed to render {scad_file.name} ({render_mode})")
|
||||
if result.stdout:
|
||||
print(result.stdout)
|
||||
if result.stderr:
|
||||
@@ -61,6 +63,7 @@ def main() -> None:
|
||||
rows = ask_int("Number of rows (rows): ")
|
||||
cell_size = ask_float("Inner cell size in mm (cell_size): ")
|
||||
tray_height = ask_float("Tray height in mm (tray_height): ")
|
||||
water_tray_height = ask_float("Water tray wall height in mm (water_tray_height): ")
|
||||
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -68,7 +71,7 @@ def main() -> None:
|
||||
if not scad_file.exists():
|
||||
raise SystemExit(f"Missing file: {scad_file}")
|
||||
|
||||
# Render the tray only
|
||||
# seedling tray
|
||||
render_scad(
|
||||
scad_file,
|
||||
OUT_DIR / "seedlings-tray.stl",
|
||||
@@ -76,10 +79,11 @@ def main() -> None:
|
||||
rows,
|
||||
cell_size,
|
||||
tray_height,
|
||||
water_tray_height,
|
||||
"tray_only",
|
||||
)
|
||||
|
||||
# Render all plates in a grid
|
||||
# removable plates
|
||||
render_scad(
|
||||
scad_file,
|
||||
OUT_DIR / "plates.stl",
|
||||
@@ -87,9 +91,34 @@ def main() -> None:
|
||||
rows,
|
||||
cell_size,
|
||||
tray_height,
|
||||
water_tray_height,
|
||||
"plates_only",
|
||||
)
|
||||
|
||||
# water tray
|
||||
render_scad(
|
||||
scad_file,
|
||||
OUT_DIR / "water-tray.stl",
|
||||
cols,
|
||||
rows,
|
||||
cell_size,
|
||||
tray_height,
|
||||
water_tray_height,
|
||||
"water_tray_only",
|
||||
)
|
||||
|
||||
# pressing tool
|
||||
render_scad(
|
||||
scad_file,
|
||||
OUT_DIR / "pusher-tool.stl",
|
||||
cols,
|
||||
rows,
|
||||
cell_size,
|
||||
tray_height,
|
||||
water_tray_height,
|
||||
"tool_only",
|
||||
)
|
||||
|
||||
print("\nDone.")
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,37 @@
|
||||
// ========================================================
|
||||
// PARAMETRIC SEEDLING TRAY & PUSHABLE PLATES
|
||||
// PARAMETRIC SEEDLING TRAY SYSTEM
|
||||
// Includes: Tray, Plates, Angled Water Tray, & Ball-Pusher Tool
|
||||
// ========================================================
|
||||
|
||||
/* [Grid Configuration] */
|
||||
cols = 3; // Number of columns
|
||||
rows = 2; // Number of rows
|
||||
cols = 4; // Number of columns
|
||||
rows = 3; // Number of rows
|
||||
cell_size = 35; // Inner width/length of each square cell (mm)
|
||||
tray_height = 50; // Total height of the tray (mm)
|
||||
|
||||
/* [Wall & Ledge Dimensions] */
|
||||
wall_thickness = 1.6; // Thickness of the walls (mm)
|
||||
wall_thickness = 1.6; // Thickness of outer and interior walls (mm)
|
||||
bottom_lip = 4.0; // Width of the bottom ledge holding the plate (mm)
|
||||
floor_thickness = 2.0; // Thickness of the bottom lip (mm)
|
||||
floor_thickness = 2.0; // Height/thickness of the bottom lip (mm)
|
||||
|
||||
/* [Pusher Plate Settings] */
|
||||
plate_thickness = 2.0; // Thickness of the pushable plate (mm)
|
||||
plate_thickness = 2.0; // Thickness of the removable plate (mm)
|
||||
clearance = 0.4; // Gap between cell walls and plate for easy sliding (mm)
|
||||
drain_hole_dia = 4.5; // Diameter of drainage holes in the pushable plate (mm)
|
||||
drain_hole_dia = 4.5; // Diameter of drainage holes in the plate (mm)
|
||||
|
||||
/* [Angled Water Tray Settings] */
|
||||
water_tray_height = 25; // Height of the bottom drip tray (mm)
|
||||
wt_clearance = 1.5; // Extra room around main tray for easy removal (mm)
|
||||
wt_flare = 3.5; // Outward wall expansion at top (mm)
|
||||
|
||||
/* [Single Pressing Tool Settings] */
|
||||
tool_plate_thick = 3.0; // Thickness of the square pusher plate (mm)
|
||||
stem_dia = 10.0; // Diameter of the stick (mm)
|
||||
stem_length = 45.0; // Length of the stick shaft (mm)
|
||||
handle_ball_dia = 20.0; // Diameter of the handle ball on top (mm)
|
||||
|
||||
/* [Render Mode] */
|
||||
// ["both_layout", "assembled", "tray_only", "plates_only"]
|
||||
render_mode = "both_layout";
|
||||
|
||||
render_mode = "all_layout"; // ["all_layout", "assembled", "tray_only", "plates_only", "water_tray_only", "tool_only"]
|
||||
|
||||
|
||||
// --- DERIVED CALCULATIONS ---
|
||||
@@ -30,28 +40,61 @@ total_width = cols * pitch + wall_thickness;
|
||||
total_length = rows * pitch + wall_thickness;
|
||||
|
||||
|
||||
// --- MAIN DISPLAY ---
|
||||
// --- MAIN DISPLAY EXECUTION ---
|
||||
if (render_mode == "tray_only") {
|
||||
seedling_tray();
|
||||
} else if (render_mode == "plates_only") {
|
||||
plate_grid();
|
||||
} else if (render_mode == "both_layout") {
|
||||
} else if (render_mode == "water_tray_only") {
|
||||
water_tray();
|
||||
} else if (render_mode == "tool_only") {
|
||||
single_pressing_tool();
|
||||
} else if (render_mode == "all_layout") {
|
||||
// Top Left: Main Tray
|
||||
seedling_tray();
|
||||
translate([0, total_length + 10, 0])
|
||||
|
||||
// Bottom Left: Plates Grid
|
||||
translate([0, -total_length - 20, 0])
|
||||
plate_grid();
|
||||
|
||||
// Top Right: Angled Water Tray
|
||||
translate([total_width + 25, 0, 0])
|
||||
water_tray();
|
||||
|
||||
// Bottom Right: Single Pressing Tool
|
||||
translate([total_width + 25 + (total_width / 2), -total_length / 2 - 20, 0])
|
||||
single_pressing_tool();
|
||||
|
||||
} else if (render_mode == "assembled") {
|
||||
// Visual preview
|
||||
seedling_tray();
|
||||
for (r = [0 : rows - 1]) {
|
||||
for (c = [0 : cols - 1]) {
|
||||
translate([
|
||||
wall_thickness + c * pitch + clearance/2,
|
||||
wall_thickness + r * pitch + clearance/2,
|
||||
floor_thickness
|
||||
])
|
||||
pusher_plate();
|
||||
// 1. Water tray at base
|
||||
color("DarkSlateGray") water_tray();
|
||||
|
||||
// 2. Seedling tray inside water tray
|
||||
translate([wall_thickness + wt_clearance/2, wall_thickness + wt_clearance/2, floor_thickness]) {
|
||||
color("ForestGreen", 0.95) seedling_tray();
|
||||
|
||||
// Plates on bottom ledges
|
||||
color("SaddleBrown") {
|
||||
for (r = [0 : rows - 1]) {
|
||||
for (c = [0 : cols - 1]) {
|
||||
translate([
|
||||
wall_thickness + c * pitch + clearance/2,
|
||||
wall_thickness + r * pitch + clearance/2,
|
||||
floor_thickness
|
||||
])
|
||||
pusher_plate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Pressing tool
|
||||
translate([
|
||||
wall_thickness + wt_clearance/2 + wall_thickness + (cell_size / 2),
|
||||
wall_thickness + wt_clearance/2 + wall_thickness + (cell_size / 2),
|
||||
-stem_length / 2
|
||||
])
|
||||
color("Goldenrod") single_pressing_tool();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,23 +102,19 @@ if (render_mode == "tray_only") {
|
||||
// MODULES
|
||||
// ========================================================
|
||||
|
||||
// Main seedling tray body
|
||||
// 1. Main seedling tray body
|
||||
module seedling_tray() {
|
||||
difference() {
|
||||
// Outer solid volume
|
||||
cube([total_width, total_length, tray_height]);
|
||||
|
||||
// Cut out each cell pocket and bottom push hole
|
||||
for (r = [0 : rows - 1]) {
|
||||
for (c = [0 : cols - 1]) {
|
||||
x_pos = wall_thickness + c * pitch;
|
||||
y_pos = wall_thickness + r * pitch;
|
||||
|
||||
// 1. Main cell compartment
|
||||
translate([x_pos, y_pos, floor_thickness])
|
||||
cube([cell_size, cell_size, tray_height]);
|
||||
|
||||
// 2. Bottom push-through opening
|
||||
translate([x_pos + bottom_lip, y_pos + bottom_lip, -1])
|
||||
cube([
|
||||
cell_size - (2 * bottom_lip),
|
||||
@@ -87,16 +126,14 @@ module seedling_tray() {
|
||||
}
|
||||
}
|
||||
|
||||
// Single pushable bottom plate
|
||||
// 2. Single removable bottom plate
|
||||
module pusher_plate() {
|
||||
p_size = cell_size - clearance;
|
||||
offset_margin = p_size / 3.5;
|
||||
|
||||
difference() {
|
||||
// Plate body
|
||||
cube([p_size, p_size, plate_thickness]);
|
||||
|
||||
// 5-points drainage hole pattern
|
||||
translate([p_size / 2, p_size / 2, -1])
|
||||
cylinder(h = plate_thickness + 2, d = drain_hole_dia, $fn = 20);
|
||||
|
||||
@@ -109,7 +146,7 @@ module pusher_plate() {
|
||||
}
|
||||
}
|
||||
|
||||
// Plate grid
|
||||
// 3. Print layout grid matching exact tray cell count
|
||||
module plate_grid() {
|
||||
p_size = cell_size - clearance;
|
||||
spacing = 2;
|
||||
@@ -120,4 +157,39 @@ module plate_grid() {
|
||||
pusher_plate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Water Tray with Angled Outward Walls
|
||||
module water_tray() {
|
||||
wt_inner_w = total_width + wt_clearance;
|
||||
wt_inner_l = total_length + wt_clearance;
|
||||
wt_outer_w = wt_inner_w + (2 * wall_thickness);
|
||||
wt_outer_l = wt_inner_l + (2 * wall_thickness);
|
||||
|
||||
difference() {
|
||||
translate([wt_outer_w / 2, wt_outer_l / 2, 0])
|
||||
linear_extrude(height = water_tray_height, scale = [(wt_outer_w + 2 * wt_flare) / wt_outer_w, (wt_outer_l + 2 * wt_flare) / wt_outer_l])
|
||||
square([wt_outer_w, wt_outer_l], center = true);
|
||||
|
||||
translate([wt_outer_w / 2, wt_outer_l / 2, floor_thickness])
|
||||
linear_extrude(height = water_tray_height, scale = [(wt_inner_w + 2 * wt_flare) / wt_inner_w, (wt_inner_l + 2 * wt_flare) / wt_inner_l])
|
||||
square([wt_inner_w, wt_inner_l], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Pressing Tool
|
||||
module single_pressing_tool() {
|
||||
p_size = (cell_size - clearance);
|
||||
|
||||
// Square pusher plate at base
|
||||
translate([0, 0, tool_plate_thick / 2])
|
||||
cube([p_size, p_size, tool_plate_thick], center = true);
|
||||
|
||||
// Vertical stick
|
||||
translate([0, 0, tool_plate_thick])
|
||||
cylinder(h = stem_length, d = stem_dia, $fn = 32);
|
||||
|
||||
// Palm ball handle on top
|
||||
translate([0, 0, tool_plate_thick + stem_length])
|
||||
sphere(d = handle_ball_dia, $fn = 40);
|
||||
}
|
||||
Reference in New Issue
Block a user