diff --git a/walls.scad b/walls.scad index ae97d58..a3235f7 100644 --- a/walls.scad +++ b/walls.scad @@ -217,6 +217,244 @@ module sparse_cuboid(size, dir=RIGHT, strut=5, maxang=30, max_bridge=20, } +// Module: hex_panel() +// Usage: +// hex_panel(shape, wall, spacing, [frame=], [bevel=], [bevel_frame=], [h=|height=|l=|length=], [anchor=], [orient=], [spin=]) +// Description: +// Produces a panel with a honeycomb interior. The panel consists of a frame containing +// a honeycob interior. The frame is laid out in the XY plane with the honeycob interior +// and then extruded to the height h. The shape argument defines the outer bounderies of +// the frame. +// . +// The simplest way to define the frame shape is to give a cuboid size as a 3d vector for +// the shape argument. The h argument is not allowed in this case. With rectangular frames you can supply the +// bevel argument which applies a 45 deg bevel on the specified list of edges. These edges +// can be LEFT, RIGHT, FRONT, or BACK to place a bevel the edge facing upward. You can add +// BOTTOM, as in LEFT+BOT, to get a bevel that faces down. When beveling a separate beveled frame +// is added to the model. You can independently control its thickness by setting `bevel_frame`, which +// defaults to the frame thickness. Note also that `frame` and `bevel_frame` can be set to zero +// to produce just the honeycomb. +// . +// The other option is to provide a 2D path as the shape argument. The path must not intersect +// itself. You must give the height argument in this case and you cannot give the bevel argument. +// The panel is made from a linear extrusion of the specified shape. In this case, anchoring +// is done as usual for linear sweeps. The shape appears by default on its base and you can +// choose "hull" or "intersect" anchor types. +// Arguments: +// shape = 3D size vector or a 2D path +// strut = thickness of hexagonal bracing +// spacing = center-to-center spacing of hex cells in the honeycomb. +// -- +// frame = width of the frame around the honeycomb. Default: same as strut +// bevel = list of edges to bevel on rectangular case when shape is a size vector; allowed options are RIGHT, LEFT, BACK, or FRONT, or those directions with BOTTOM added. Default: [] +// bevel_frame = width of the frame applied at bevels. Default: same as frame +// h / height / l / length = thickness of the panel when shape is a path +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` for rectangular panels, `"zcenter"` for extrusions. +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +// atype = Select "hull", "intersect" anchor types. Default: "hull" +// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" +// Extra Anchors: +// "base" = Anchor to the base of the shape in its native position +// "top" = Anchor to the top of the shape in its native position +// "zcenter" = Center shape in the Z direction in the native XY position (default) +// Anchor Types: +// hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. +// intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings. +// Examples +// hex_panel([50, 100, 5], strut=1.5, spacing=10); +// hex_panel([50, 100, 5], 1.5, 10, frame = 5); +// hex_panel([50, 100, 5], 5, 10.05); +// hex_panel([50, 100, 5], 1.5, 20, frame = 5); +// hex_panel([50, 100, 5], 1.5, 12, frame = 0); +// hex_panel([50, 100, 5], frame = 10, spacing = 20, strut = 4); +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [LEFT, RIGHT]); +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD, BACK]); +// hex_panel([50, 100, 10], 1.5, 10, frame = 3, bevel = [LEFT, RIGHT, FWD, BACK]); +// hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [LEFT, RIGHT, FWD+BOTTOM, BACK+BOTTOM]); +// hex_panel([50, 100, 10], 1.5, 10, frame=2, bevel_frame=0, bevel = [FWD, BACK+BOT, RIGHT, LEFT]); +// Example: Triangle +// s = [[0, -40], [0, 40], [60, 0]]; +// hex_panel(s, strut=1.5, spacing=10, h = 10, frame = 5); +// Example: Concave polygon +// s = [[0, -40], [0, 70], [60, 0], [80, 20], [70, -20]]; +// hex_panel(s, 1.5, 10, h = 10, frame = 5); +// Example: Another concave example +// s = [[0, -40], [0, 40], [30, 20], [60, 40], [60, -40], [30, -20]]; +// hex_panel(s, 1.5, 10, h = 10, frame = 5); +// Example: Circular panel +// hex_panel(circle(30), 1.5, 10, h = 10, frame = 5); +// Example: More complicated shape +// s = glued_circles(d=50, spread=50, tangent=30); +// hex_panel(s, 1.5, 10, h = 10, frame = 5); +// Example: Care is required when arranging panels vertically for 3d printability. Setting `orient=RIGHT` produces the correct result. +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT); +// zrot(-90)hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD, BACK], anchor = FWD + RIGHT + BOTTOM, orient = RIGHT); +// Example: In this example panels one of the panels is positioned with `orient=FWD` which produces hexagons with 60 deg overhang edges that may not be 3d printable. This example alsu uses `bevel_frame` to thin the material at the corner. +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=1, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT); +// hex_panel([100, 50, 10], 1.5, 10, frame = 5, bevel_frame=1, bevel = [LEFT, RIGHT], anchor = FWD + LEFT + BOTTOM, orient = FWD); +// Example: Joining panels with {{attach()}}. In this case panels were joined front beveled edge to back beveled edge, which means the hex structure doesn't align at the joint +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=0, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT) +// attach(BACK,FRONT) +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=0, bevel = [FWD, BACK]); +// Example: Joining panels with {{attach()}}. Attaching BACK to BACK aligns the hex structure which looks better. +// hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT) +// attach(BACK,BACK) +// hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [FWD, BACK]); +module hex_panel( + shape, + strut, + spacing, + frame, + bevel_frame, + h, height, l, length, + bevel = [], + anchor, + orient = UP, cp="centroid", atype="hull", + spin = 0) +{ + frame = first_defined([frame,strut]); + bevel_frame = first_defined([bevel_frame, frame]); + shape = force_path(shape,"shape"); + bevel = is_vector(bevel) ? [bevel] : bevel; + bevOK = len([for(bev=bevel) if (norm([bev.x,bev.y])==1 && (bev.x==0 || bev.y==0) && (bev.z==0 || bev.z==-1)) 1]) == len(bevel); + dummy= + assert(is_finite(strut) && strut > 0, "strut must be positive") + assert(is_finite(frame) && frame >= 0, "frame must be nonnegative") + assert(is_finite(bevel_frame) && bevel_frame >= 0, "bevel_frame must be nonnegative") + assert(is_finite(spacing) && spacing>0, "spacing must be positive") + assert(is_path(shape,2) || is_vector(shape, 3), "shape must be a path or a 3D vector") + assert(len(bevel) == 0 || is_vector(shape, 3), "bevel must be used only on rectangular panels") + assert(is_path(shape) || all_positive(shape), "when shape is a size vector all components must be positive") + assert(bevOK, "bevel list contains an invalid entry") + assert(!(in_list(FRONT, bevel) && in_list(FRONT+BOTTOM, bevel)), "conflicting FRONT bevels") + assert(!(in_list(BACK, bevel) && in_list(BACK+BOTTOM, bevel)), "conflicting BACK bevels") + assert(!(in_list(RIGHT, bevel) && in_list(RIGHT+BOTTOM, bevel)), "conflicting RIGHT bevels") + assert(!(in_list(LEFT, bevel) && in_list(LEFT+BOTTOM, bevel)), "conflicting LEFT bevels") + assert(is_undef(h) || is_path(shape), "cannot give h with a size vector"); + shp = is_path(shape) ? shape : square([shape.x, shape.y], center = true); + ht = is_path(shape) ? one_defined([h,l,height,length],"height,length,l,h") + : shape.z; + + bounds = pointlist_bounds(shp); + sizes = bounds[1] - bounds[0]; // [xsize, ysize] + assert(frame*2 + spacing < sizes[0], "There must be room for at least 1 cell in the honeycomb"); + assert(frame*2 + spacing < sizes[1], "There must be room for at least 1 cell in the honeycomb"); + + bevpaths = len(bevel)==0 ? [] + : _bevelSolid(shape,bevel); + if (len(bevel) > 0) { + size1 = [bevpaths[0][0].x-bevpaths[0][1].x, bevpaths[0][2].y-bevpaths[0][1].y,ht]; + size2 = [bevpaths[1][0].x-bevpaths[1][1].x, bevpaths[1][2].y-bevpaths[1][1].y]; + shift = point2d(centroid(bevpaths[1])-centroid(bevpaths[0])); + offset = (centroid(bevpaths[0])); + attachable(anchor,spin,orient,size=size1,size2=size2,shift=shift,offset=offset){ + down(ht/2) + intersection() { + union() { + linear_extrude(height = ht, convexity=8) { + _honeycomb(shp, spacing = spacing, hex_wall = strut); + offset_stroke(shp, width=[-frame, 0], closed=true); + } + for (b = bevel) _bevelWall(shape, b, bevel_frame); + } + vnf_polyhedron(vnf_vertex_array(bevpaths, col_wrap=true, caps=true)); + } + children(); + } + } + else if (is_vector(shape)){ + attachable(anchor = anchor, spin = spin, orient = orient, size = shape) { + down(ht/2) + linear_extrude(height = ht, convexity=8) { + _honeycomb(shp, spacing = spacing, hex_wall = strut); + offset_stroke(shp, width=[-frame, 0], closed=true); + } + children(); + } + } + else { + anchors = [ + named_anchor("zcenter", [0,0,0], UP), + named_anchor("base", [0,0,-ht/2], UP), + named_anchor("top", [0,0,ht/2], UP) + ]; + attachable(anchor = default(anchor,"zcenter"), spin = spin, orient = orient, path=shp, h=ht, cp=cp, extent=atype=="hull",anchors=anchors) { + down(ht/2) + linear_extrude(height = ht, convexity=8) { + _honeycomb(shp, spacing = spacing, hex_wall = strut); + offset_stroke(shp, width=[-frame, 0], closed=true); + } + children(); + } + + } +} + + +module _honeycomb(shape, spacing=10, hex_wall=1) +{ + hex = hexagon(id=spacing-hex_wall, spin=180/6); + bounds = pointlist_bounds(shape); + size = bounds[1] - bounds[0]; + hex_rgn2 = grid_copies(spacing=spacing, size=size, stagger=true, p=hex); + center = (bounds[0] + bounds[1]) / 2; + hex_rgn = move(center, p=hex_rgn2); + difference(){ + polygon(shape); + region(hex_rgn); + } +} + + +function _bevelSolid(shape, bevel) = + let( + tX = in_list(RIGHT, bevel) ? -shape.z : 0, + tx = in_list(LEFT, bevel) ? shape.z : 0, + tY = in_list(BACK, bevel) ? -shape.z : 0, + ty = in_list(FRONT, bevel) ? shape.z : 0, + bX = in_list(RIGHT + BOTTOM, bevel) ? -shape.z : 0, + bx = in_list(LEFT + BOTTOM, bevel) ? shape.z : 0, + bY = in_list(BACK + BOTTOM, bevel) ? -shape.z : 0, + by = in_list(FRONT + BOTTOM, bevel) ? shape.z : 0, + pathB = path3d(rect(select(shape,0,1)) + [[bX,by],[bx,by],[bx,bY],[bX,bY]]), + pathT = path3d(rect(select(shape,0,1)) + [[tX,ty],[tx,ty],[tx,tY],[tX,tY]],shape.z) + ) + [pathB,pathT]; + +module _bevelWall(shape, bevel, thickness) { + + l = bevel.y != 0 ? shape.x : shape.y; + d = bevel.y != 0 ? shape.y : shape.x; + zr = bevel.y == -1 ? 180 + : bevel.y == 1 ? 0 + : bevel.x == -1 ? 90 + : bevel.x == 1 ? 270 + : undef; + xr = bevel.x != 0 && bevel.z < 0 ? 180 : 0; + yr = bevel.y != 0 && bevel.z < 0 ? 180 : 0; + + path = [[-thickness, 0], [0, 0], [-shape.z, -shape.z], [-shape.z-thickness, -shape.z]]; + + up(shape.z/2) + xrot(xr) yrot(yr) zrot(zr) down(shape.z/2) + back(d/2) right(l/2) + zrot(90) xrot(-90) + linear_extrude(l) polygon(path); +} + +include +//corrugated_wall(h=50, l=100, strut=0); + + +// corrugated wall: strut = width of frame +// note on intersection() etc about slowness and avoid use if possible + +//echo(os=offset_stroke(circle(r=10),closed=false, width=0)); + + + + // Module: corrugated_wall() // Synopsis: Makes a corrugated rectangular wall. // SynTags: Geom @@ -235,7 +473,7 @@ module sparse_cuboid(size, dir=RIGHT, strut=5, maxang=30, max_bridge=20, // l = length of strut wall. // thick = thickness of strut wall. // --- -// strut = the width of the cross-braces. +// strut = the width of the frame. // wall = thickness of corrugations. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`