1//////////////////////////////////////////////////////////////////////
  2// LibFile: walls.scad
  3//   Walls and structural elements that 3D print without support.
  4// Includes:
  5//   include <BOSL2/std.scad>
  6//   include <BOSL2/walls.scad>
  7// FileGroup: Parts
  8// FileSummary: Walls and structural elements that 3D print without support.
  9//////////////////////////////////////////////////////////////////////
 10
 11
 12include<rounding.scad>
 13
 14// Section: Walls
 15
 16
 17// Module: sparse_wall()
 18// Synopsis: Makes an open cross-braced rectangular wall.
 19// SynTags: Geom
 20// Topics: FDM Optimized, Walls
 21// See Also: hex_panel(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut()
 22//
 23// Usage:
 24//   sparse_wall(h, l, thick, [maxang=], [strut=], [max_bridge=]) [ATTACHMENTS];
 25//
 26// Description:
 27//   Makes an open rectangular strut with X-shaped cross-bracing, designed to reduce
 28//   the need for support material in 3D printing.
 29//
 30// Arguments:
 31//   h = height of strut wall.
 32//   l = length of strut wall.
 33//   thick = thickness of strut wall.
 34//   ---
 35//   maxang = maximum overhang angle of cross-braces, measured down from vertical.  Default: 30 
 36//   strut = the width of the cross-braces. Default: 5
 37//   max_bridge = maximum bridging distance between cross-braces.  Default: 20
 38//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 39//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 40//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 41//
 42// See Also: corrugated_wall(), thinning_wall()
 43//
 44// Example: Typical Shape
 45//   sparse_wall(h=40, l=100, thick=3);
 46// Example: Thinner Strut
 47//   sparse_wall(h=40, l=100, thick=3, strut=2);
 48// Example: Larger maxang
 49//   sparse_wall(h=40, l=100, thick=3, strut=2, maxang=45);
 50// Example: Longer max_bridge
 51//   sparse_wall(h=40, l=100, thick=3, strut=2, maxang=45, max_bridge=30);
 52module sparse_wall(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, anchor=CENTER, spin=0, orient=UP)
 53{
 54    zoff = h/2 - strut/2;
 55    yoff = l/2 - strut/2;
 56
 57    maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang);
 58    maxz = 2 * maxhyp * cos(maxang);
 59
 60    zreps = ceil(2*zoff/maxz);
 61    zstep = 2*zoff / zreps;
 62
 63    hyp = zstep/2 / cos(maxang);
 64    maxy = min(2 * hyp * sin(maxang), max_bridge+strut);
 65
 66    yreps = ceil(2*yoff/maxy);
 67
 68    size = [thick, l, h];
 69    attachable(anchor,spin,orient, size=size) {
 70        yrot(90) {
 71            linear_extrude(height=thick, convexity=4*yreps, center=true) {
 72                sparse_wall2d([h,l], maxang=maxang, strut=strut, max_bridge=max_bridge);
 73            }
 74        }
 75        children();
 76    }
 77}
 78
 79
 80// Module: sparse_wall2d()
 81// Synopsis: Makes an open cross-braced rectangular wall.
 82// SynTags: Geom
 83// Topics: FDM Optimized, Walls
 84// See Also: sparse_wall(), hex_panel(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut()
 85//
 86// Usage:
 87//   sparse_wall2d(size, [maxang=], [strut=], [max_bridge=]) [ATTACHMENTS];
 88//
 89// Description:
 90//   Makes a 2D open rectangular square with X-shaped cross-bracing, designed to be extruded, to make a strut that reduces
 91//   the need for support material in 3D printing.
 92//
 93// Arguments:
 94//   size = The `[X,Y]` size of the outer rectangle.
 95//   ---
 96//   maxang = maximum overhang angle of cross-braces.
 97//   strut = the width of the cross-braces.
 98//   max_bridge = maximum bridging distance between cross-braces.
 99//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
100//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
101//
102// See Also: corrugated_wall(), thinning_wall()
103//
104// Example: Typical Shape
105//   sparse_wall2d(size=[40,100]);
106// Example: Thinner Strut
107//   sparse_wall2d(size=[40,100], strut=2);
108// Example: Larger maxang
109//   sparse_wall2d(size=[40,100], strut=2, maxang=45);
110// Example: Longer max_bridge
111//   sparse_wall2d(size=[40,100], strut=2, maxang=45, max_bridge=30);
112module sparse_wall2d(size=[50,100], maxang=30, strut=5, max_bridge=20, anchor=CENTER, spin=0)
113{
114    h = size.x;
115    l = size.y;
116
117    zoff = h/2 - strut/2;
118    yoff = l/2 - strut/2;
119
120    maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang);
121    maxz = 2 * maxhyp * cos(maxang);
122
123    zreps = ceil(2*zoff/maxz);
124    zstep = 2*zoff / zreps;
125
126    hyp = zstep/2 / cos(maxang);
127    maxy = min(2 * hyp * sin(maxang), max_bridge+strut);
128
129    yreps = ceil(2*yoff/maxy);
130    ystep = 2*yoff / yreps;
131
132    ang = atan(ystep/zstep);
133    len = zstep / cos(ang);
134    attachable(anchor,spin, two_d=true, size=size) {
135        union() {
136            difference() {
137                square([h, l], center=true);
138                square([h-2*strut, l-2*strut], center=true);
139            }
140            ycopies(ystep, n=yreps) {
141                xcopies(zstep, n=zreps) {
142                    skew(syx=tan(-ang)) square([(h-strut)/zreps, strut/cos(ang)], center=true);
143                    skew(syx=tan( ang)) square([(h-strut)/zreps, strut/cos(ang)], center=true);
144                }
145            }
146        }
147        children();
148    }
149}
150
151
152// Module: sparse_cuboid()
153// Synopsis: Makes an open cross-braced cuboid
154// SynTags: Geom
155// Topics: FDM Optimized, Walls
156// See Also: sparse_wall(), hex_panel(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut(), cuboid()
157// Usage:
158//   sparse_cuboid(size, [dir], [maxang=], [struct=]
159// Description:
160//   Makes an open rectangular cuboid with X-shaped cross-bracing to reduce the need for material in 3d printing.
161//   The direction of the cross bracing can be aligned with the X, Y or Z axis.  This module can be
162//   used as a drop-in replacement for {{cuboid()}} if you belatedly decide that your model would benefit from
163//   the sparse construction.  Note that for Z aligned bracing the max_bridge parameter contrains the gaps that are parallel
164//   to the Y axis, and the angle is measured relative to the X direction.  
165// Arguments:
166//   size = The size of sparse wall, a number or length 3 vector.
167//   dir = direction of holes through the cuboid, must be a vector parallel to the X, Y or Z axes, or one of "X", "Y" or "Z".  Default: "Y"
168//   ---
169//   maxang = maximum overhang angle of cross-braces, measured down from vertical.  Default: 30 
170//   strut = the width of the cross-braces. Default: 5
171//   max_bridge = maximum bridging distance between cross-braces.  Default: 20
172//   chamfer = Size of chamfer, inset from sides.  Default: No chamfering.
173//   rounding = Radius of the edge rounding.  Default: No rounding.
174//   edges = Edges to mask.  See [Specifying Edges](attachments.scad#section-specifying-edges).  Default: all edges.
175//   except = Edges to explicitly NOT mask.  See [Specifying Edges](attachments.scad#section-specifying-edges).  Default: No edges.
176//   trimcorners = If true, rounds or chamfers corners where three chamfered/rounded edges meet.  Default: `true`
177//   teardrop = If given as a number, rounding around the bottom edge of the cuboid won't exceed this many degrees from vertical.  If true, the limit angle is 45 degrees.  Default: `false`
178//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
179//   spin = Rotate this many degrees around the Z axis.  See [spin](attachments.scad#subsection-spin).  Default: `0`
180//   orient = Vector to rotate top towards.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
181// Examples:
182//   sparse_cuboid([10,20,30], strut=1);
183//   sparse_cuboid([10,20,30], "Y", strut=1);
184//   sparse_cuboid([10,20,30], UP, strut=1);
185//   sparse_cuboid(30, FWD, strut=2, rounding=2, $fn=24);
186module sparse_cuboid(size, dir=RIGHT, strut=5, maxang=30, max_bridge=20,
187    chamfer,
188    rounding,
189    edges=EDGES_ALL,
190    except=[],
191    except_edges,
192    trimcorners=true,
193    teardrop=false,
194    anchor=CENTER, spin=0, orient=UP)
195{
196  size = scalar_vec3(size);
197  dummy1=assert(in_list(dir,["X","Y","Z"]) || is_vector(dir,3), "dir must be a 3-vector or one of \"X\", \"Y\", or \"Z\"");
198  count = len([for(d=dir) if (d!=0) d]);
199  dummy2=assert(is_string(dir) || (count==1 && len(dir)<=3), "vector valued dir must have exactly one non-zero component");
200  dir = is_string(dir) ? dir
201      : dir.x ? "X"
202      : dir.y ? "Y"
203      : "Z";
204  attachable(anchor,spin,orient,size=size){
205    intersection(){
206      if (dir=="X")
207         sparse_wall(size.z,size.y,size.x,strut=strut,maxang=maxang, max_bridge=max_bridge);
208      else if (dir=="Y")
209         zrot(90)
210           sparse_wall(size.z,size.x,size.y,strut=strut,maxang=maxang, max_bridge=max_bridge);
211      else
212         yrot(90)
213           sparse_wall(size.x,size.y,size.z,strut=strut,maxang=maxang, max_bridge=max_bridge);
214      cuboid(size=size, chamfer=chamfer, rounding=rounding,edges=edges, except=except, except_edges=except_edges,
215           trimcorners=trimcorners, teardrop=teardrop);
216    }
217    children();
218  }    
219}
220
221
222// Module: hex_panel()
223// Synopsis: Create a hexagon braced panel of any shape
224// SynTags: Geom
225// Topics: FDM Optimized, Walls
226// See Also: sparse_wall(), hex_panel(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut()
227// Usage:
228//   hex_panel(shape, wall, spacing, [frame=], [bevel=], [bevel_frame=], [h=|height=|l=|length=], [anchor=], [orient=], [spin=])
229// Description:
230//   Produces a panel with a honeycomb interior that can be rectangular with optional beveling, or
231//   an arbitrary polygon shape without beveling. The panel consists of a frame containing
232//   a honeycob interior. The frame is laid out in the XY plane with the honeycob interior 
233//   and then extruded to the height h. The shape argument defines the outer bounderies of
234//   the frame.
235//   .
236//   The simplest way to define the frame shape is to give a cuboid size as a 3d vector for
237//   the shape argument.  The h argument is not allowed in this case.  With rectangular frames you can supply the
238//   bevel argument which applies a 45 deg bevel on the specified list of edges.  These edges
239//   can be LEFT, RIGHT, FRONT, or BACK to place a bevel the edge facing upward.  You can add
240//   BOTTOM, as in LEFT+BOT, to get a bevel that faces down.  When beveling a separate beveled frame
241//   is added to the model.  You can independently control its thickness by setting `bevel_frame`, which
242//   defaults to the frame thickness.  Note also that `frame` and `bevel_frame` can be set to zero
243//   to produce just the honeycomb.  
244//   . 
245//   The other option is to provide a 2D path as the shape argument. The path must not intersect
246//   itself.  You must give the height argument in this case and you cannot give the bevel argument.
247//   The panel is made from a linear extrusion of the specified shape.  In this case, anchoring
248//   is done as usual for linear sweeps.  The shape appears by default on its base and you can
249//   choose "hull" or "intersect" anchor types.  
250// Arguments:
251//   shape = 3D size vector or a 2D path
252//   strut = thickness of hexagonal bracing
253//   spacing = center-to-center spacing of hex cells in the honeycomb.
254//   ---
255//   frame = width of the frame around the honeycomb.  Default: same as strut
256//   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: []
257//   bevel_frame = width of the frame applied at bevels.  Default: same as frame
258//   h / height / l / length = thickness of the panel when shape is a path 
259//   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.  
260//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
261//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
262//   atype = Select "hull", "intersect" anchor types.  Default: "hull"
263//   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"
264// Named Anchors:
265//   "base" = Anchor to the base of the shape in its native position
266//   "top" = Anchor to the top of the shape in its native position
267//   "zcenter" = Center shape in the Z direction in the native XY position (default)
268// Anchor Types:
269//   hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. 
270//   intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings.
271// Examples:
272//     hex_panel([50, 100, 5], strut=1.5, spacing=10);
273//     hex_panel([50, 100, 5], 1.5, 10, frame = 5);
274//     hex_panel([50, 100, 5], 5, 10.05);
275//     hex_panel([50, 100, 5], 1.5, 20, frame = 5);
276//     hex_panel([50, 100, 5], 1.5, 12, frame = 0);
277//     hex_panel([50, 100, 5], frame = 10, spacing = 20, strut = 4);
278//     hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [LEFT, RIGHT]);
279//     hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD,  BACK]);
280//     hex_panel([50, 100, 10], 1.5, 10, frame = 3, bevel = [LEFT, RIGHT, FWD, BACK]);
281//     hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [LEFT, RIGHT, FWD+BOTTOM, BACK+BOTTOM]);
282//     hex_panel([50, 100, 10], 1.5, 10, frame=2, bevel_frame=0, bevel = [FWD, BACK+BOT, RIGHT, LEFT]);
283// Example: Triangle
284//     s = [[0, -40], [0, 40], [60, 0]];
285//     hex_panel(s, strut=1.5, spacing=10, h = 10, frame = 5); 
286// Example: Concave polygon
287//     s = [[0, -40], [0, 70], [60, 0], [80, 20], [70, -20]];
288//     hex_panel(s, 1.5, 10, h = 10, frame = 5); 
289// Example: Another concave example
290//     s = [[0, -40], [0, 40], [30, 20], [60, 40], [60, -40], [30, -20]];
291//     hex_panel(s, 1.5, 10, h = 10, frame = 5); 
292// Example: Circular panel
293//     hex_panel(circle(30), 1.5, 10, h = 10, frame = 5);
294// Example: More complicated shape
295//     s = glued_circles(d=50, spread=50, tangent=30);
296//     hex_panel(s, 1.5, 10, h = 10, frame = 5);
297// Example: Care is required when arranging panels vertically for 3d printability.  Setting `orient=RIGHT` produces the correct result. 
298//     hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT);
299//     zrot(-90)hex_panel([50, 100, 10], 1.5, 10, frame = 5,  bevel = [FWD, BACK], anchor = FWD + RIGHT + BOTTOM, orient = RIGHT);
300// 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.  
301//     hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=1, bevel = [FWD,  BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT);
302//     hex_panel([100, 50, 10], 1.5, 10, frame = 5, bevel_frame=1, bevel = [LEFT, RIGHT], anchor = FWD + LEFT + BOTTOM, orient = FWD);
303// 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
304//     hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=0, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT)
305//       attach(BACK,FRONT) 
306//          hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=0, bevel = [FWD, BACK]);
307// Example: Joining panels with {{attach()}}.  Attaching BACK to BACK aligns the hex structure which looks better.  
308//     hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT)
309//       attach(BACK,BACK) 
310//          hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [FWD, BACK]);
311module hex_panel(
312    shape,
313    strut,
314    spacing,
315    frame,
316    bevel_frame,
317    h, height, l, length, 
318    bevel = [],
319    anchor, 
320    orient = UP, cp="centroid", atype="hull",
321    spin = 0) 
322{
323    frame = first_defined([frame,strut]);
324    bevel_frame = first_defined([bevel_frame, frame]);
325    shape = force_path(shape,"shape");
326    bevel = is_vector(bevel) ? [bevel] : bevel;
327    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);
328    dummy=
329      assert(is_finite(strut) && strut > 0, "strut must be positive")
330      assert(is_finite(frame) && frame >= 0, "frame must be nonnegative")
331      assert(is_finite(bevel_frame) && bevel_frame >= 0, "bevel_frame must be nonnegative")
332      assert(is_finite(spacing) && spacing>0, "spacing must be positive")
333      assert(is_path(shape,2) || is_vector(shape, 3), "shape must be a path or a 3D vector")
334      assert(len(bevel) == 0 || is_vector(shape, 3), "bevel must be used only on rectangular panels")
335      assert(is_path(shape) || all_positive(shape), "when shape is a size vector all components must be positive")
336      assert(bevOK, "bevel list contains an invalid entry")
337      assert(!(in_list(FRONT, bevel) && in_list(FRONT+BOTTOM, bevel)), "conflicting FRONT bevels")
338      assert(!(in_list(BACK,  bevel) && in_list(BACK+BOTTOM,  bevel)), "conflicting BACK bevels")
339      assert(!(in_list(RIGHT, bevel) && in_list(RIGHT+BOTTOM, bevel)), "conflicting RIGHT bevels")
340      assert(!(in_list(LEFT,  bevel) && in_list(LEFT+BOTTOM,  bevel)), "conflicting LEFT bevels")
341      assert(is_undef(h) || is_path(shape), "cannot give h with a size vector");
342    shp = is_path(shape) ? shape : square([shape.x, shape.y], center = true);
343    ht = is_path(shape) ? one_defined([h,l,height,length],"height,length,l,h")
344       : shape.z;
345    
346    bounds = pointlist_bounds(shp);
347    sizes = bounds[1] - bounds[0]; // [xsize, ysize]
348    assert(frame*2 + spacing < sizes[0], "There must be room for at least 1 cell in the honeycomb");
349    assert(frame*2 + spacing < sizes[1], "There must be room for at least 1 cell in the honeycomb");
350
351    bevpaths = len(bevel)==0 ? []
352             : _bevelSolid(shape,bevel);
353    if (len(bevel) > 0) {
354         size1 = [bevpaths[0][0].x-bevpaths[0][1].x, bevpaths[0][2].y-bevpaths[0][1].y,ht];
355         size2 = [bevpaths[1][0].x-bevpaths[1][1].x, bevpaths[1][2].y-bevpaths[1][1].y];
356         shift = point2d(centroid(bevpaths[1])-centroid(bevpaths[0]));
357         offset = (centroid(bevpaths[0]));
358         attachable(anchor,spin,orient,size=size1,size2=size2,shift=shift,offset=offset){
359             down(ht/2)
360                 intersection() {
361                     union() {
362                         linear_extrude(height = ht, convexity=8) {
363                             _honeycomb(shp, spacing = spacing, hex_wall = strut);
364                             offset_stroke(shp, width=[-frame, 0], closed=true);
365                         }
366                         for (b = bevel) _bevelWall(shape, b, bevel_frame);
367                     }
368                     vnf_polyhedron(vnf_vertex_array(bevpaths, col_wrap=true, caps=true));
369                 }
370             children();
371         }
372     }
373     else if (is_vector(shape)){
374         attachable(anchor = anchor, spin = spin, orient = orient, size = shape) {        
375             down(ht/2) 
376                 linear_extrude(height = ht, convexity=8) {
377                     _honeycomb(shp, spacing = spacing, hex_wall = strut);
378                     offset_stroke(shp, width=[-frame, 0], closed=true);
379                 }
380             children();
381         }
382    }
383    else {
384         anchors = [
385           named_anchor("zcenter", [0,0,0], UP),
386           named_anchor("base", [0,0,-ht/2], UP),
387           named_anchor("top", [0,0,ht/2], UP)          
388         ];
389         attachable(anchor = default(anchor,"zcenter"), spin = spin, orient = orient, path=shp, h=ht, cp=cp, extent=atype=="hull",anchors=anchors) {        
390              down(ht/2) 
391                 linear_extrude(height = ht, convexity=8) {
392                     _honeycomb(shp, spacing = spacing, hex_wall = strut);
393                     offset_stroke(shp, width=[-frame, 0], closed=true);
394                 }
395             children();
396         }
397
398    } 
399}
400
401
402module _honeycomb(shape, spacing=10, hex_wall=1) 
403{
404        hex = hexagon(id=spacing-hex_wall, spin=180/6);
405        bounds = pointlist_bounds(shape);
406        size = bounds[1] - bounds[0];
407        hex_rgn2 = grid_copies(spacing=spacing, size=size, stagger=true, p=hex);
408        center = (bounds[0] + bounds[1]) / 2;
409        hex_rgn = move(center, p=hex_rgn2);
410        difference(){
411            polygon(shape);
412            region(hex_rgn);
413        }
414}
415
416
417function _bevelSolid(shape, bevel) =
418  let(
419    tX = in_list(RIGHT,          bevel) ? -shape.z : 0,
420    tx = in_list(LEFT,           bevel) ?  shape.z : 0,
421    tY = in_list(BACK,           bevel) ? -shape.z : 0,
422    ty = in_list(FRONT,          bevel) ?  shape.z : 0,
423    bX = in_list(RIGHT + BOTTOM, bevel) ? -shape.z : 0,
424    bx = in_list(LEFT  + BOTTOM, bevel) ?  shape.z : 0,
425    bY = in_list(BACK  + BOTTOM, bevel) ? -shape.z : 0,
426    by = in_list(FRONT + BOTTOM, bevel) ?  shape.z : 0,
427    pathB = path3d(rect(select(shape,0,1)) + [[bX,by],[bx,by],[bx,bY],[bX,bY]]),
428    pathT = path3d(rect(select(shape,0,1)) + [[tX,ty],[tx,ty],[tx,tY],[tX,tY]],shape.z)
429  )
430  [pathB,pathT];
431
432module _bevelWall(shape, bevel, thickness) {
433
434    l = bevel.y != 0 ? shape.x : shape.y;
435    d = bevel.y != 0 ? shape.y : shape.x;
436    zr = bevel.y == -1 ? 180 
437       : bevel.y ==  1 ? 0 
438       : bevel.x == -1 ? 90 
439       : bevel.x ==  1 ? 270 
440       : undef;
441    xr = bevel.x != 0 && bevel.z < 0 ? 180 : 0;
442    yr = bevel.y != 0 && bevel.z < 0 ? 180 : 0;
443    
444    path = [[-thickness, 0], [0, 0], [-shape.z, -shape.z], [-shape.z-thickness, -shape.z]];
445
446    up(shape.z/2)
447    xrot(xr) yrot(yr) zrot(zr) down(shape.z/2)
448      back(d/2) right(l/2) 
449      zrot(90) xrot(-90)
450        linear_extrude(l) polygon(path);
451}
452
453
454// Module: corrugated_wall()
455// Synopsis: Makes a corrugated rectangular wall.
456// SynTags: Geom
457// Topics: FDM Optimized, Walls
458// See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut()
459//
460// Usage:
461//   corrugated_wall(h, l, thick, [strut=], [wall=]) [ATTACHMENTS];
462//
463// Description:
464//   Makes a corrugated wall which relieves contraction stress while still
465//   providing support strength.  Designed with 3D printing in mind.
466//
467// Arguments:
468//   h = height of strut wall.
469//   l = length of strut wall.
470//   thick = thickness of strut wall.
471//   ---
472//   strut = the width of the frame.
473//   wall = thickness of corrugations.
474//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
475//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
476//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
477//
478// See Also: sparse_wall(), thinning_wall()
479//
480// Example: Typical Shape
481//   corrugated_wall(h=50, l=100);
482// Example: Wider Strut
483//   corrugated_wall(h=50, l=100, strut=8);
484// Example: Thicker Wall
485//   corrugated_wall(h=50, l=100, strut=8, wall=3);
486module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, anchor=CENTER, spin=0, orient=UP)
487{
488    amplitude = (thick - wall) / 2;
489    period = min(15, thick * 2);
490    steps = quantup(segs(thick/2),4);
491    step = period/steps;
492    il = l - 2*strut + 2*step;
493    size = [thick, l, h];
494    attachable(anchor,spin,orient, size=size) {
495        union() {
496            linear_extrude(height=h-2*strut+0.1, slices=2, convexity=ceil(2*il/period), center=true) {
497                polygon(
498                    points=concat(
499                        [for (y=[-il/2:step:il/2]) [amplitude*sin(y/period*360)-wall/2, y] ],
500                        [for (y=[il/2:-step:-il/2]) [amplitude*sin(y/period*360)+wall/2, y] ]
501                    )
502                );
503            }
504            difference() {
505                cube([thick, l, h], center=true);
506                cube([thick+0.5, l-2*strut, h-2*strut], center=true);
507            }
508        }
509        children();
510    }
511}
512
513
514// Module: thinning_wall()
515// Synopsis: Makes a rectangular wall with a thin middle.
516// SynTags: Geom
517// Topics: FDM Optimized, Walls
518// See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut()
519//
520// Usage:
521//   thinning_wall(h, l, thick, [ang=], [braces=], [strut=], [wall=]) [ATTACHMENTS];
522//
523// Description:
524//   Makes a rectangular wall which thins to a smaller width in the center,
525//   with angled supports to prevent critical overhangs.
526//
527// Arguments:
528//   h = Height of wall.
529//   l = Length of wall.  If given as a vector of two numbers, specifies bottom and top lengths, respectively.
530//   thick = Thickness of wall.
531//   ---
532//   ang = Maximum overhang angle of diagonal brace.
533//   braces = If true, adds diagonal crossbraces for strength.
534//   strut = The width of the borders and diagonal braces.  Default: `thick/2`
535//   wall = The thickness of the thinned portion of the wall.  Default: `thick/2`
536//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
537//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
538//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
539//
540// See Also: sparse_wall(), corrugated_wall(), thinning_triangle()
541//
542// Example: Typical Shape
543//   thinning_wall(h=50, l=80, thick=4);
544// Example: Trapezoidal
545//   thinning_wall(h=50, l=[80,50], thick=4);
546// Example: Trapezoidal with Braces
547//   thinning_wall(h=50, l=[80,50], thick=4, strut=4, wall=2, braces=true);
548module thinning_wall(h=50, l=100, thick=5, ang=30, braces=false, strut, wall, anchor=CENTER, spin=0, orient=UP)
549{
550    l1 = (l[0] == undef)? l : l[0];
551    l2 = (l[1] == undef)? l : l[1];
552    strut = is_num(strut)? strut : min(h,l1,l2,thick)/2;
553    wall = is_num(wall)? wall : thick/2;
554
555    bevel_h = strut + (thick-wall)/2/tan(ang);
556    cp1 = circle_2tangents(strut, [0,0,+h/2], [l2/2,0,+h/2], [l1/2,0,-h/2])[0];
557    cp2 = circle_2tangents(bevel_h, [0,0,+h/2], [l2/2,0,+h/2], [l1/2,0,-h/2])[0];
558    cp3 = circle_2tangents(bevel_h, [0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,+h/2])[0];
559    cp4 = circle_2tangents(strut, [0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,+h/2])[0];
560
561    z1 = h/2;
562    z2 = cp1.z;
563    z3 = cp2.z;
564
565    x1 = l2/2;
566    x2 = cp1.x;
567    x3 = cp2.x;
568    x4 = l1/2;
569    x5 = cp4.x;
570    x6 = cp3.x;
571
572    y1 = thick/2;
573    y2 = wall/2;
574
575    corner1 = [ x2, 0,  z2];
576    corner2 = [-x5, 0, -z2];
577    brace_len = norm(corner1-corner2);
578
579    size = [l1, thick, h];
580    attachable(anchor,spin,orient, size=size, size2=[l2,thick]) {
581        zrot(90) {
582            polyhedron(
583                points=[
584                    [-x4, -y1, -z1],
585                    [ x4, -y1, -z1],
586                    [ x1, -y1,  z1],
587                    [-x1, -y1,  z1],
588
589                    [-x5, -y1, -z2],
590                    [ x5, -y1, -z2],
591                    [ x2, -y1,  z2],
592                    [-x2, -y1,  z2],
593
594                    [-x6, -y2, -z3],
595                    [ x6, -y2, -z3],
596                    [ x3, -y2,  z3],
597                    [-x3, -y2,  z3],
598
599                    [-x4,  y1, -z1],
600                    [ x4,  y1, -z1],
601                    [ x1,  y1,  z1],
602                    [-x1,  y1,  z1],
603
604                    [-x5,  y1, -z2],
605                    [ x5,  y1, -z2],
606                    [ x2,  y1,  z2],
607                    [-x2,  y1,  z2],
608
609                    [-x6,  y2, -z3],
610                    [ x6,  y2, -z3],
611                    [ x3,  y2,  z3],
612                    [-x3,  y2,  z3],
613                ],
614                faces=[
615                    [ 4,  5,  1],
616                    [ 5,  6,  2],
617                    [ 6,  7,  3],
618                    [ 7,  4,  0],
619
620                    [ 4,  1,  0],
621                    [ 5,  2,  1],
622                    [ 6,  3,  2],
623                    [ 7,  0,  3],
624
625                    [ 8,  9,  5],
626                    [ 9, 10,  6],
627                    [10, 11,  7],
628                    [11,  8,  4],
629
630                    [ 8,  5,  4],
631                    [ 9,  6,  5],
632                    [10,  7,  6],
633                    [11,  4,  7],
634
635                    [11, 10,  9],
636                    [20, 21, 22],
637
638                    [11,  9,  8],
639                    [20, 22, 23],
640
641                    [16, 17, 21],
642                    [17, 18, 22],
643                    [18, 19, 23],
644                    [19, 16, 20],
645
646                    [16, 21, 20],
647                    [17, 22, 21],
648                    [18, 23, 22],
649                    [19, 20, 23],
650
651                    [12, 13, 17],
652                    [13, 14, 18],
653                    [14, 15, 19],
654                    [15, 12, 16],
655
656                    [12, 17, 16],
657                    [13, 18, 17],
658                    [14, 19, 18],
659                    [15, 16, 19],
660
661                    [ 0,  1, 13],
662                    [ 1,  2, 14],
663                    [ 2,  3, 15],
664                    [ 3,  0, 12],
665
666                    [ 0, 13, 12],
667                    [ 1, 14, 13],
668                    [ 2, 15, 14],
669                    [ 3, 12, 15],
670                ],
671                convexity=6
672            );
673            if(braces) {
674                bracepath = [
675                    [-strut*0.33,thick/2],
676                    [ strut*0.33,thick/2],
677                    [ strut*0.33+(thick-wall)/2/tan(ang), wall/2],
678                    [ strut*0.33+(thick-wall)/2/tan(ang),-wall/2],
679                    [ strut*0.33,-thick/2],
680                    [-strut*0.33,-thick/2],
681                    [-strut*0.33-(thick-wall)/2/tan(ang),-wall/2],
682                    [-strut*0.33-(thick-wall)/2/tan(ang), wall/2]
683                ];
684                xflip_copy() {
685                    intersection() {
686                        extrude_from_to(corner1,corner2) {
687                            polygon(bracepath);
688                        }
689                        prismoid([l1,thick],[l2,thick],h=h,anchor=CENTER);
690                    }
691                }
692            }
693        }
694        children();
695    }
696}
697
698
699// Module: thinning_triangle()
700// Synopsis: Makes a triangular wall with a thin middle.
701// SynTags: Geom
702// Topics: FDM Optimized, Walls
703// See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut()
704//
705// Usage:
706//   thinning_triangle(h, l, thick, [ang=], [strut=], [wall=], [diagonly=], [center=]) [ATTACHMENTS];
707//
708// Description:
709//   Makes a triangular wall with thick edges, which thins to a smaller width in
710//   the center, with angled supports to prevent critical overhangs.
711//
712// Arguments:
713//   h = height of wall.
714//   l = length of wall.
715//   thick = thickness of wall.
716//   ---
717//   ang = maximum overhang angle of diagonal brace.
718//   strut = the width of the diagonal brace.
719//   wall = the thickness of the thinned portion of the wall.
720//   diagonly = boolean, which denotes only the diagonal side (hypotenuse) should be thick.
721//   center = If true, centers shape.  If false, overrides `anchor` with `UP+BACK`.
722//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
723//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
724//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
725//
726// See Also: thinning_wall()
727//
728// Example: Centered
729//   thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=true);
730// Example: All Braces
731//   thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=false);
732// Example: Diagonal Brace Only
733//   thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, diagonly=true, center=false);
734module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly=false, center, anchor, spin=0, orient=UP)
735{
736    dang = atan(h/l);
737    dlen = h/sin(dang);
738    size = [thick, l, h];
739    anchor = get_anchor(anchor, center, BOT+FRONT, CENTER);
740    attachable(anchor,spin,orient, size=size) {
741        difference() {
742            union() {
743                if (!diagonly) {
744                    translate([0, 0, -h/2])
745                        narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
746                    translate([0, -l/2, 0])
747                        xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
748                }
749                intersection() {
750                    cube(size=[thick, l, h], center=true);
751                    xrot(-dang) yrot(180) {
752                        narrowing_strut(w=thick, l=dlen*1.2, wall=strut, ang=ang);
753                    }
754                }
755                cube(size=[wall, l-0.1, h-0.1], center=true);
756            }
757            xrot(-dang) {
758                translate([0, 0, h/2]) {
759                    cube(size=[thick+0.1, l*2, h], center=true);
760                }
761            }
762        }
763        children();
764    }
765}
766
767
768// Module: narrowing_strut()
769// Synopsis: Makes a strut like an extruded baseball home plate.
770// SynTags: Geom
771// Topics: FDM Optimized
772// See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut()
773//
774// Usage:
775//   narrowing_strut(w, l, wall, [ang=]) [ATTACHMENTS];
776//
777// Description:
778//   Makes a rectangular strut with the top side narrowing in a triangle.
779//   The shape created may be likened to an extruded home plate from baseball.
780//   This is useful for constructing parts that minimize the need to support
781//   overhangs.
782//
783// Arguments:
784//   w = Width (thickness) of the strut.
785//   l = Length of the strut.
786//   wall = height of rectangular portion of the strut.
787//   ---
788//   ang = angle that the trianglar side will converge at.
789//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
790//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
791//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
792//
793// Example:
794//   narrowing_strut(w=10, l=100, wall=5, ang=30);
795module narrowing_strut(w=10, l=100, wall=5, ang=30, anchor=BOTTOM, spin=0, orient=UP)
796{
797    h = wall + w/2/tan(ang);
798    size = [w, l, h];
799    attachable(anchor,spin,orient, size=size) {
800        xrot(90)
801        fwd(h/2) {
802            linear_extrude(height=l, center=true, slices=2) {
803                back(wall/2) square([w, wall], center=true);
804                back(wall-0.001) {
805                    yscale(1/tan(ang)) {
806                        difference() {
807                            zrot(45) square(w/sqrt(2), center=true);
808                            fwd(w/2) square(w, center=true);
809                        }
810                    }
811                }
812            }
813        }
814        children();
815    }
816}
817
818
819
820// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap