"""Classic clay-pot look for the pole planter: round tapered body, rim band with two rings. All units mm. Same insert, pole channel and V-rails as ../plantpot.py (imported, unchanged); only the pot's outside differs. Prints upright as modelled, no supports.""" import math import sys from pathlib import Path from build123d import * sys.path.append(str(Path(__file__).resolve().parents[1])) from plantpot import (insert, ch_wall, band, channel, v_rail, pole_slot, CH_X, POT_Y0, POT_H, POT_WALL, FLOOR, FIT, R, col_x, col_y0, col_y1, flare, g_d, g_half, band_t, wall_top, CH_BACK, rrect, INS_WALL, z_rail, RAIL_HALF) from printcheck import overhangs from outputs import out R_RIM = 80.0 # rim band radius (vertical band) R_BOT = 56.0 # body radius at the foot: classic taper (~7.5 deg) BAND_H = 34.0 # rim band height STEP = 6.0 # band sticks out this far past the body top; 45 deg-ish underside COVER_T = 1.6 # seam cover skin RING_Z, RING_D = (8.0, 13.0), 0.8 # decorative rings on the band: height above its bottom, depth # centre the circle so the rim's front meets the channel edge at the original front face, where the insert's front tab closes it CY = POT_Y0 + math.sqrt(R_RIM ** 2 - (CH_X / 2) ** 2) z_band = POT_H - BAND_H r_body = R_RIM - STEP z_step = z_band - STEP * 1.1 # underside a bit steeper than 45 deg def revolved(pts): """Revolve an (r, z) outline around the pot's vertical axis.""" face = Plane.XZ * Polygon(*pts, align=None) return revolve(face, Axis.Z).moved(Location((0, CY))) outer = revolved([(0, 0), (R_BOT, 0), (r_body, z_step), (R_RIM, z_band), (R_RIM, POT_H), (0, POT_H)]) cavity = revolved([(0, FLOOR), (R_BOT - POT_WALL, FLOOR), (r_body - POT_WALL, z_step), (R_RIM - POT_WALL, z_band), (R_RIM - POT_WALL, POT_H + 1), (0, POT_H + 1)]) rings = [revolved([(R_RIM + 0.1, z_band + z - RING_D * 1.3), (R_RIM - RING_D, z_band + z), (R_RIM + 0.1, z_band + z + RING_D * 1.3)]) for z in RING_Z] pot = outer - cavity + ((ch_wall + band) & outer) - channel - rings for s in (1, -1): pot -= v_rail(s * CH_X / 2, g_d, g_half, POT_Y0 - 1, col_y1 - R - flare + 2 * FIT) # channel walls stop at wall_top inside the pot: one soil space above them pot -= Box(CH_X + 2 * band_t + 2, CH_BACK + band_t + 1 - POT_Y0, POT_H, align=(Align.CENTER, Align.MIN, Align.MIN)).moved(Location((0, POT_Y0, wall_top))) & cavity floor_region = Circle(R_BOT - POT_WALL).moved(Location((0, CY))) - Rectangle(CH_X + 2 * band_t, 2 * (CH_BACK + band_t)) # drain holes in rings round the centre, ~12 apart, kept clear of the walls and the channel keep = offset(floor_region, -4) holes = [Circle(2).moved(Location((r * math.cos(a), CY + r * math.sin(a)))) for r in range(6, int(R_BOT), 12) for a in [2 * math.pi * i / max(1, round(2 * math.pi * r / 12)) for i in range(max(1, round(2 * math.pi * r / 12)))]] holes = [h for h in holes if (h - keep).area < 1e-6] n_pot = len(holes) pot -= extrude(Plane.XY.offset(-1) * (Sketch() + holes), FLOOR + 2) pot.label = "pot" # seam covers: U-cap over the joint between the insert's front tab and the rim end; slides down over the rim, FIT clear of both faces. # Below the rail tops nothing changes in plan view, so a straight skirt slides on; printed lid down. col_cy = (col_y0 + col_y1) / 2 obst = offset(rrect(col_x, col_y1 - col_y0, col_cy, R + flare), FIT) + Circle(R_RIM + FIT).moved(Location((0, CY))) window = Rectangle(14, col_y0 - 3 - (POT_Y0 + 6), align=(Align.MIN, Align.MIN)).moved(Location((CH_X / 2 - 6, col_y0 - 3))) hug = offset(obst, COVER_T) & window soil = (rrect(col_x - 2 * INS_WALL, col_y1 - col_y0 - 2 * INS_WALL, col_cy, R + flare - INS_WALL) + Circle(R_RIM - POT_WALL).moved(Location((0, CY))) + Rectangle(CH_X, 100, align=(Align.CENTER, Align.MIN)).moved(Location((0, POT_Y0 - FIT)))) skirt_z = z_rail + RAIL_HALF + 0.5 # outer skirt stops above the rails inner = (offset(soil, -FIT) - offset(soil, -FIT - COVER_T)) & window # inner skirt on the soil side: clamps the rim between the skirts cover = (extrude(Plane.XY.offset(skirt_z) * (hug - obst), POT_H - skirt_z) + extrude(Plane.XY.offset(wall_top + FIT) * inner, POT_H - wall_top - FIT) # inside, the collar and channel walls go on below wall_top + extrude(Plane.XY.offset(POT_H) * (hug - offset(soil, -FIT - COVER_T)), COVER_T)) cover_r, cover_l = cover, mirror(cover, Plane.YZ) cover_r.label, cover_l.label = "seam_cover_r", "seam_cover_l" assert pot.is_valid and len(pot.solids()) == 1 assert not overhangs(pot), "pot needs supports printed upright" assert (insert & pot).volume < 1e-3, "insert and pot collide" assert (pot & pole_slot).volume < 1e-3, "pot blocks the pole" assert (insert & pot.moved(Location((0, 0, -1)))).volume > 1, "pot doesn't hang on the rails" for cv in (cover_r, cover_l): assert cv.is_valid and len(cv.solids()) == 1, cv.label assert (cv & pot).volume < 1e-3 and (cv & insert).volume < 1e-3, f"{cv.label} collides" assert not overhangs(cv, down=(0, 0, 1)), f"{cv.label} needs supports printed lid down" if __name__ == "__main__": from viewer import make_viewer export_step(Compound(label="clay_pot", children=[pot, insert, cover_r, cover_l]), out("clay_pot.step")) export_stl(pot, out("clay_pot.stl")) export_stl(insert, out("insert.stl")) for cv in (cover_r, cover_l): # flipped: lid on the bed export_stl(cv.rotate(Axis.X, 180), out(cv.label + ".stl")) make_viewer([(pot, "#c8714a"), (insert, "#6b8f71"), (cover_r, "#c8714a"), (cover_l, "#c8714a")], title="Clay pot") print("ok", pot.bounding_box().size, round(pot.volume / 1000, 1), "cm3", n_pot, "holes, soil", round((cavity.volume) / 1000, 1), "cm3 approx")