mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-12-09 15:29:09 +00:00
Merge branch 'BelfrySCAD:master' into assets
This commit is contained in:
commit
ba190ecd37
11 changed files with 435 additions and 164 deletions
|
|
@ -1,7 +1,7 @@
|
|||
DocsDirectory: BOSL2.wiki/
|
||||
TargetProfile: githubwiki
|
||||
ProjectName: The Belfry OpenScad Library, v2. (BOSL2)
|
||||
GenerateDocs: Files, TOC, Index, Topics, CheatSheet, Sidebar
|
||||
GenerateDocs: Files, TOC, Index, Topics, CheatSheet, Sidebar, Glossary
|
||||
SidebarHeader:
|
||||
## Indices
|
||||
.
|
||||
|
|
|
|||
|
|
@ -767,6 +767,7 @@ function _make_anchor_legal(anchor,geom) =
|
|||
// This module differs from {{position()}} and {{align()}} in that it rotates the children to
|
||||
// the anchor direction, which generally means it places the children on the surface of a parent.
|
||||
// There are two modes of operation, parent anchor (single argument) and parent-child anchor (double argument).
|
||||
// In most cases you should use the parent-child (double argument) version of `attach()`.
|
||||
// .
|
||||
// The parent-child anchor (double argument) version is usually easier to use, and it is more powerful because it supports
|
||||
// alignment. You provide an anchor on the parent (`parent`) and an anchor on the child (`child`).
|
||||
|
|
@ -810,17 +811,6 @@ function _make_anchor_legal(anchor,geom) =
|
|||
// ignored** with the **double argument** version of `attach()`. As noted above, you can give `spin=` to the
|
||||
// child but using the `spin=` parameter to `attach()` is more likely to be useful.
|
||||
// .
|
||||
// For the single parameter version of `attach()` you give only the `parent` anchor. The `align` direction
|
||||
// is not permitted. In this case the child is placed at the specified parent anchor point
|
||||
// and rotated to the anchor direction. For example, `attach(TOP) cuboid(2);` will place a small
|
||||
// cube **with its center** located at the TOP anchor of the parent, so just half the cube will project
|
||||
// from the parent. If you want the cube sitting on the parent you need to anchor the cube to its bottom:
|
||||
// `attach(TOP) cuboid(2,anchor=BOT);`.
|
||||
// .
|
||||
// The **single argument** version of `attach()` **respects `anchor=` and `orient=` given to the child.**
|
||||
// These options will probably be necessary, in fact, to get the child correctly positioned. Note that
|
||||
// giving `spin=` to `attach()` in this case is the same as applying `zrot()` to the child.
|
||||
// .
|
||||
// You can overlap attached children into the parent by giving the `$overlap` value
|
||||
// which is 0 by default, or by the `overlap=` argument. This is to prevent OpenSCAD
|
||||
// from making non-manifold objects. You can define `$overlap=` as an argument in a parent
|
||||
|
|
@ -833,6 +823,17 @@ function _make_anchor_legal(anchor,geom) =
|
|||
// the parent. For an inside child this is equivalent to giving a positive overlap and negative inset value.
|
||||
// For a child with `inside=false` it is equivalent to a negative overlap and negative inset.
|
||||
// .
|
||||
// The single parameter version of `attach()` is rarely needed; to use it, you give only the `parent` anchor. The `align` direction
|
||||
// is not permitted. In this case the child is placed at the specified parent anchor point
|
||||
// and rotated to the anchor direction. For example, `attach(TOP) cuboid(2);` will place a small
|
||||
// cube **with its center** located at the TOP anchor of the parent, so just half the cube will project
|
||||
// from the parent. If you want the cube sitting on the parent you need to anchor the cube to its bottom:
|
||||
// `attach(TOP) cuboid(2,anchor=BOT);`.
|
||||
// .
|
||||
// The **single argument** version of `attach()` **respects `anchor=` and `orient=` given to the child.**
|
||||
// These options will probably be necessary, in fact, to get the child correctly positioned. Note that
|
||||
// giving `spin=` to `attach()` in this case is the same as applying `zrot()` to the child.
|
||||
// .
|
||||
// For a step-by-step explanation of
|
||||
// attachments, see the [Attachments Tutorial](Tutorial-Attachments).
|
||||
// Arguments:
|
||||
|
|
|
|||
|
|
@ -1267,7 +1267,6 @@ function bezier_vnf(patches=[], splinesteps=16, style="default") =
|
|||
: assert(false,"\nInvalid patch list.")
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
|
||||
// Function: bezier_vnf_degenerate_patch()
|
||||
|
|
@ -1283,7 +1282,8 @@ function bezier_vnf(patches=[], splinesteps=16, style="default") =
|
|||
// equal. If the resulting patch has no faces then returns an empty VNF. Note that due to the degeneracy,
|
||||
// the shape of the surface can be triangular even though the underlying patch is a rectangle.
|
||||
// If you specify return_edges then the return is a list whose first element is the VNF and whose second
|
||||
// element lists the edges in the order [left, right, top, bottom], where each list is a list of the actual
|
||||
// element lists the edges in the order [left (index zero of rows), right (last index of rows), top (first row), bottom (last row)],
|
||||
// where each list is a list of the actual
|
||||
// point values, but possibly only a single point if that edge is degenerate.
|
||||
// The method checks for various types of degeneracy and uses a triangular or partly triangular array of sample points.
|
||||
// See examples below for the types of degeneracy detected and how the patch is sampled for those cases.
|
||||
|
|
@ -1292,7 +1292,7 @@ function bezier_vnf(patches=[], splinesteps=16, style="default") =
|
|||
// patch = Patch to process
|
||||
// splinesteps = Number of segments to produce on each side. Default: 16
|
||||
// reverse = reverse direction of faces. Default: false
|
||||
// return_edges = if true return the points on the four edges: [left, right, top, bottom]. Default: false
|
||||
// return_edges = if true return the points on the four edges of the array: [left (index zero of rows), right (last index of rows) , top (first row), bottom (last row)]. Default: false
|
||||
// Example(3D,NoAxes): This quartic patch is degenerate at one corner, where a row of control points are equal. Processing this degenerate patch normally produces excess triangles near the degenerate point.
|
||||
// splinesteps=8;
|
||||
// patch=[
|
||||
|
|
|
|||
170
hinges.scad
170
hinges.scad
|
|
@ -21,19 +21,28 @@ include <screws.scad>
|
|||
// Usage:
|
||||
// knuckle_hinge(length, offset, segs, [inner], [arm_height=], [arm_angle=], [fill=], [clear_top=], [gap=], [round_top=], [round_bot=], [knuckle_diam=], [pin_diam=], [pin_fn=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
|
||||
// Description:
|
||||
// Construct standard knuckle hinge in two parts using a hinge pin that must be separately supplied.
|
||||
// The default is configured to use a piece of 1.75 mm filament as the hinge pin, but you can select
|
||||
// any dimensions you like to use a screw or other available pin material. The BOTTOM of the hinge
|
||||
// is its mount point, which is aligned with the hinge pin centersurface, and the hinge pin hole is
|
||||
// the CENTER of the hinge. The offset is the distance from a vertical mounting point to the center
|
||||
// of the hinge pin. The hinge barrel is held by an angled support and vertical support. The
|
||||
// Construct standard knuckle hinge in two parts using a hinge pin that must be separately supplied,
|
||||
// or a print-in-place knuckle hinge. The default is configured to use a piece of 1.75 mm filament
|
||||
// as the hinge pin, but you can select any dimensions you like to use a screw or other available pin material.
|
||||
// The hinge appears with what is typically the mounting surface restong on the XY plane with the hinge rotational axis
|
||||
// parallel to the X axis. The BOTTOM
|
||||
// of the hinge is its mount point, which, if clearance is not set, is in line with the hinge pin rotational center.
|
||||
// In this case the hinge pin hole is the CENTER of the hinge.
|
||||
// The offset is the distance the base (the mounting point) to the center
|
||||
// of the hinge pin. The offset cannot be smaller than the knuckle diameter.
|
||||
// The hinge barrel is held by an angled support and vertical support. The
|
||||
// length of the angled support is determined by its angle and the offset. You specify the length
|
||||
// of the vertical support with the arm_height parameter.
|
||||
// .
|
||||
// A hinge requires clearance so its parts don't interfere. If the hinge pin is exactly centered on
|
||||
// the top of your part, then the hinge may not close all the way due to interference at the edge.
|
||||
// A small clearance, specified with `clearance=`, raises the hinge up and can ease this
|
||||
// interference. It should probably be equal to a layer thickness or two. If the hinge knuckle is
|
||||
// A small clearance, specified with `clearance=`, move the hinge in the Y direction (which would be UP if
|
||||
// it were mounted on the side of a cube). This shifts the rotation slightly and can ease the interference.
|
||||
// It should probably be equal to a layer thickness or two. Note that clearance moves the rotational center
|
||||
// but the CENTER, BOTTOM and TOP anchors stay fixed, so if you give a nonzero clearance, the center of rotation
|
||||
// will be offset by the clearance from the center anchor.
|
||||
// .
|
||||
// If the hinge knuckle is
|
||||
// close to the hinged part then the mating part may interfere. You can create clearance to address
|
||||
// this problem by increasing the offset to move the hinge knuckles farther away. Another method is
|
||||
// to cut out a curved recess on the parts to allow space for the other hinges. This is possible
|
||||
|
|
@ -41,6 +50,13 @@ include <screws.scad>
|
|||
// room for the hinge knuckles. It must be positive for any space to be cut, and to use this option
|
||||
// you must make the hinge a child of some object and specify {{diff()}} for the parent object of
|
||||
// the hinge.
|
||||
// .
|
||||
// To create a print-in-place hinge set `in_place=true` to create a hinge with interlocking cones
|
||||
// instead of leaving a hole for an inserted pin. In this case, `pin_diam` gives the diameter of
|
||||
// the base of the cones and defaults to 1 less than the knuckle diameter. You can also set `in_place`
|
||||
// to a cone angle. This is the angle of the bottom edge of the cone, measured from the vertical---the overhang
|
||||
// angle for printing. A larger angle produces a pointier cone that is more difficult to print and has
|
||||
// a smaller clearance gap inside.
|
||||
// Figure(2D,Med,NoScales): The basic hinge form appears on the left. If fill is set to true the gap between the mount surface and hinge arm is filled as shown on the right.
|
||||
// _knuckle_hinge_profile(4, 5, $fn=32, fill=false);
|
||||
// right(13)_knuckle_hinge_profile(4, 5, $fn=32, fill=true);
|
||||
|
|
@ -85,8 +101,26 @@ include <screws.scad>
|
|||
// a clearance hole is created through most of the hinge with a self-tap hole for the last segment.
|
||||
// If the last segment is very long you may shrink the self-tap portion using the tap_depth parameter.
|
||||
// The pin hole diameter is enlarged by the `2*$slop` for numerically specified holes.
|
||||
// Screw holes are made using {{screw_hole()}} which enlarges the hole by `4*$slop`.
|
||||
// Screw holes are made using {{screw_hole()}} which enlarges the hole by `4*$slop`.
|
||||
// .
|
||||
// Instead of a hinge pin you can set `in_place=true` to produce a print-in-place hinge that uses
|
||||
// cones to interlock the hinge segments. The default cones are 45 degrees; you can set `in_place` to an
|
||||
// angle from the vertical to adjust the cone angle. (This means larger angles are pointier, and also less likely to print successfully.)
|
||||
// Use `gap` to adjust the clearance in the hinge
|
||||
// to get something that separates after printing. When you adjust the cone hangle, higher angles result in a smaller
|
||||
// clearance where the cones meet for the same gap size, so you can somewhat adjust the tightness of the hinge
|
||||
// by changing the cone angle. The default cone diameter, which is controlled by `pin_diam` is 1 unit smaller than the knuckle diameter, which should work well
|
||||
// for larger hinges, but for small hinges you may want to specify a larger `pin_diam`.
|
||||
// Figure(3D,Small,NoScales): A print-in-place hinge with part of the hinge cut away so you can see the interlocking cones that hold it together:
|
||||
// difference()
|
||||
// {
|
||||
// union(){
|
||||
// knuckle_hinge(length=35, segs=9, offset=3.1, in_place=true,$fn=32);
|
||||
// zrot(180)knuckle_hinge(length=35, inner=true, segs=9, offset=3.1, in_place=true,$fn=32);
|
||||
// }
|
||||
// up(3)cuboid(100, anchor=LEFT+BOT);
|
||||
// }
|
||||
// Continues:
|
||||
// To blend hinges better with a model you can round off the joint with the mounting surface using
|
||||
// the `round_top` and `round_bot` parameters, which specify the cut distance, the amount of material to add.
|
||||
// They make a continuous curvature "smooth" roundover with `k=0.8`. See [smooth roundovers](rounding.scad#section-types-of-roundovers) for more
|
||||
|
|
@ -98,8 +132,8 @@ include <screws.scad>
|
|||
// right(.5)fwd(-3)color("blue")text("round_bot=1.5",size=1);
|
||||
// Arguments:
|
||||
// length = total length of the entire hinge
|
||||
// offset = horizontal offset of the hinge pin center from the mount point
|
||||
// segs = number of hinge segments
|
||||
// offset = horizontal offset of the hinge pin center from the mount point
|
||||
// inner = set to true for the "inner" hinge. Default: false
|
||||
// ---
|
||||
// arm_height = vertical height of the arm that holds the hinge barrel. Default: 0
|
||||
|
|
@ -110,15 +144,16 @@ include <screws.scad>
|
|||
// round_top = rounding amount to add where top of hinge arm joins the mount surface. Generally only useful when fill=false. Default: 0
|
||||
// round_bot = rounding amount to add where bottom of hinge arm joins the mount surface. Default: 0
|
||||
// knuckle_diam = diameter of hinge barrel. Default: 4
|
||||
// pin_diam = diameter of hinge pin hole as a number of screw specification. Default: 1.75
|
||||
// pin_diam = for regular hinges, diameter of hinge pin hole as a numerical dimension or as a screw specification. For print-in-place hinges, the diameter of the base of the interlocking cones inside the hinge. Default: 1.75 for regular hinges, 1 less than knuckle_diameter for print-in-place hinges.
|
||||
// pin_fn = $fn value to use for the pin.
|
||||
// teardrop = Set to true or UP/DOWN/FWD/BACK to specify teardrop shape for the pin hole. Default: false
|
||||
// screw_head = screw head to use for countersink
|
||||
// screw_tolerance = screw hole tolerance. Default: "close"
|
||||
// tap_depth = Don't make the tapped part of the screw hole larger than this.
|
||||
// tap_depth = Don't make the tapped part of the screw hole larger than this.
|
||||
// in_place = If true create a print-in-place hinge with 45 deg angle interlocking cones. If set to an angle, measures the angle from the vertical to the lower cone angle. Default: false
|
||||
// $slop = increases pin hole diameter
|
||||
// clearance = raises pin hole to create clearance at the edge of the mounted surface. Default: 0.15
|
||||
// clear_knuckle = clear space for hinge knuckle of mating part. Must use with {{diff()}}. Default: 0
|
||||
// knuckle_clearance = clear space to create specified clearance for hinge knuckle of mating part. Must use with {{diff()}}. Default: 0
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `BOTTOM`
|
||||
// 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`
|
||||
|
|
@ -216,19 +251,88 @@ include <screws.scad>
|
|||
// cuboid([4,40,15])
|
||||
// position(TOP+RIGHT) orient(anchor=RIGHT)
|
||||
// knuckle_hinge(length=35, segs=5, offset=2, inner=true, knuckle_clearance=1);
|
||||
// Example(3D,NoScales,VPR=[57.80,0.00,308.00],VPD=54.24,VPT=[2.34,0.76,0.15]): If you want the hinge leaves to fold flat together, pick a hinge configuration that places the centerline of the hinge pin on the plane of the hinge leaf. Hinges that fold entirely flat probably won't work, so we add some clearance between the leaves.
|
||||
// $fn=32;
|
||||
// thickness=2;
|
||||
// clearance=0.2;
|
||||
// zrot_copies([0,180])
|
||||
// color(["green","gold"][$idx])
|
||||
// diff()
|
||||
// fwd(clearance/2)
|
||||
// cuboid([20,thickness,7],anchor=BACK)
|
||||
// down(thickness/3)
|
||||
// position(TOP+BACK)
|
||||
// knuckle_hinge(20, segs=5, offset=thickness+clearance,
|
||||
// inner=$idx==0, knuckle_clearance=clearance,
|
||||
// clearance=clearance/2, arm_angle=90,
|
||||
// knuckle_diam=2*thickness+clearance,
|
||||
// clear_top=true);
|
||||
// Example(3D,NoScales,VPR=[55.00,0.00,25.00],VPD=82.67,VPT=[9.42,-0.23,1.39]): Here's a print-in-place hinge positioned for printing. The next three examples show some different ways to position a hinge like this. In this case, we create the hinge leaf, put the hinge on it, put the second hinge leaf next to it, and put the hinge on that. It would be hard to rotate this hinge. This hinge works with a 0.2mm layer height on a Prusa MK3S.
|
||||
// $fn=64;
|
||||
// leaf_gap = 0.4;
|
||||
// seg_gap = 0.2;
|
||||
// module myhinge(inner)
|
||||
// knuckle_hinge(length=25, segs=7, offset=3.1, inner=inner, in_place=true,
|
||||
// clearance=leaf_gap/2, round_bot=0.5, gap=seg_gap, seg_ratio=1/3);
|
||||
// cuboid([20,25,2],rounding=7,edges=[LEFT+FWD,LEFT+BACK]){
|
||||
// position(TOP+RIGHT) orient(UP,-90)
|
||||
// myhinge(false);
|
||||
// align(RIGHT) right(leaf_gap) cuboid([20,25,2],rounding=7,edges=[RIGHT+FWD,RIGHT+BACK])
|
||||
// position(TOP+LEFT) orient(UP,90)
|
||||
// myhinge(true);
|
||||
// }
|
||||
// Example(3D,VPR=[66.20,0.00,66.30],VPD=60.27,VPT=[0.01,-0.19,-0.36],NoAxes): A very small print-in-place hinge with a 2mm hinge barrel that printed successfully on a Prusa MK3S with 0.15 mm layer height. The 0.15 clearance places the rotation axis above the hinge leaves which enables the hinge to close all the way. This construction makes the hinge leaf a child of the hinge, which enables easy rotation of hinge leaf and anything connected to it. We do have to adjust the rotation center for the clearance. This construction also makes the leaves symmetrically so they can be made with identical code, instead of needing to round opposite ends.
|
||||
// $fn = 64;
|
||||
// diam = 2; // Hinge knuckle diameter
|
||||
// seg_gap = 0.15; // Gap between hinge segments
|
||||
// clear = 0.15; // Clearance so hinge will close all the way
|
||||
// ang=0; // Hinge rotation angle
|
||||
// module myhinge(inner)
|
||||
// knuckle_hinge(length=25, segs=11,offset=1.2, inner=inner, clearance=clear, knuckle_diam=diam,
|
||||
// pin_diam=1.8, arm_angle=28, gap=seg_gap, in_place=true, anchor=CTR,clip=2+clear)
|
||||
// children();
|
||||
// module leaf() cuboid([25,2,12],anchor=TOP+BACK,rounding=7,edges=[BOT+LEFT,BOT+RIGHT]);
|
||||
// xrot(90){ // Rotate to printing orientation
|
||||
// myhinge(true) position(BOT) leaf();
|
||||
// color("lightblue")
|
||||
// xrot(180-ang,cp=[0,clear,0])
|
||||
// zrot(180,cp=[0,clear,0])
|
||||
// myhinge(false) position(BOT) leaf();
|
||||
// }
|
||||
// Example(3D,NoAxes,VPR=[72.50,0.00,117.40],VPD=113.40,VPT=[5.64,1.70,6.16]): Here is a print-in-place hinge where the hinge barrel matches the thickness of the leaves. In this hinge we show another way to mate the parts where we {{attach()}} one hinge section to the other and the leaves are children of the hinges. This hinge printed successfully (with ang=0) on a Prusa MK3S with a 0.2mm layer thickness. This hinge is shown folded at an angle; for printing, set `ang=0`.
|
||||
// $fn=64;
|
||||
// thickness=4;
|
||||
// seg_gap = 0.2;
|
||||
// end_space = 0.6;
|
||||
// ang=35;
|
||||
// module myhinge(inner)
|
||||
// knuckle_hinge(length=25, segs=13,offset=thickness/2+end_space, inner=inner, clearance=-thickness/2, knuckle_diam=thickness,
|
||||
// arm_angle=45, gap=seg_gap, in_place=true, clip=thickness/2)
|
||||
// children();
|
||||
// module leaf() cuboid([25,thickness,25],anchor=TOP+BACK, rounding=7, edges=[BOT+LEFT,BOT+RIGHT]);
|
||||
// xrot(90)
|
||||
// myhinge(true){
|
||||
// position(BOT) leaf();
|
||||
// color("lightblue")
|
||||
// up(end_space) attach(BOT,TOP,inside=true)
|
||||
// tag("") // cancel default "remove" tag
|
||||
// xrot(-ang,cp=[0,-thickness/2,thickness/2]) myhinge(false)
|
||||
// position(BOT) leaf();
|
||||
// }
|
||||
|
||||
function knuckle_hinge(length, segs, offset, inner=false, arm_height=0, arm_angle=45, gap=0.2,
|
||||
seg_ratio=1, knuckle_diam=4, pin_diam=1.75, fill=true, clear_top=false,
|
||||
round_bot=0, round_top=0, pin_fn, clearance,
|
||||
round_bot=0, round_top=0, pin_fn, clearance, in_place=false, clip,
|
||||
tap_depth, screw_head, screw_tolerance="close",
|
||||
anchor=BOT,orient,spin) = no_function("hinge");
|
||||
|
||||
module knuckle_hinge(length, segs, offset, inner=false, arm_height=0, arm_angle=45, gap=0.2,
|
||||
seg_ratio=1, knuckle_diam=4, pin_diam=1.75, fill=true, clear_top=false,
|
||||
round_bot=0, round_top=0, pin_fn, clearance=0, teardrop,
|
||||
seg_ratio=1, knuckle_diam=4, pin_diam, fill=true, clear_top=false,
|
||||
round_bot=0, round_top=0, pin_fn, clearance=0, teardrop, in_place=false, clip,
|
||||
tap_depth, screw_head, screw_tolerance="close", knuckle_clearance,
|
||||
anchor=BOT,orient,spin)
|
||||
{
|
||||
pin_diam = default(pin_diam, in_place==false ? 1.75 : knuckle_diam-1);
|
||||
dummy =
|
||||
assert(is_str(pin_diam) || all_positive([pin_diam]), "pin_diam must be a screw spec string or a positive number")
|
||||
assert(all_positive(length), "length must be a postive number")
|
||||
|
|
@ -239,6 +343,7 @@ module knuckle_hinge(length, segs, offset, inner=false, arm_height=0, arm_angle=
|
|||
segs2 = floor(segs/2);
|
||||
seglen1 = gap + (length-(segs-1)*gap) / (segs1 + segs2*seg_ratio);
|
||||
seglen2 = gap + (length-(segs-1)*gap) / (segs1 + segs2*seg_ratio) * seg_ratio;
|
||||
numsegs = inner?segs2:segs1;
|
||||
z_adjust = segs%2==1 ? 0
|
||||
: inner? seglen1/2
|
||||
: seglen2/2;
|
||||
|
|
@ -289,11 +394,32 @@ module knuckle_hinge(length, segs, offset, inner=false, arm_height=0, arm_angle=
|
|||
{
|
||||
multmatrix(transform)
|
||||
force_tag() difference() {
|
||||
zcopies(n=inner?segs2:segs1, spacing=seglen1+seglen2)
|
||||
linear_extrude((inner?seglen2:seglen1)-gap,center=true)
|
||||
_knuckle_hinge_profile(offset=offset, arm_height=arm_height, arm_angle=arm_angle, knuckle_diam=knuckle_diam, pin_diam=pin_diam,
|
||||
zcopies(n=numsegs, spacing=seglen1+seglen2){
|
||||
linear_extrude((inner?seglen2:seglen1)-gap,center=true,convexity=4)
|
||||
_knuckle_hinge_profile(offset=offset, arm_height=arm_height, arm_angle=arm_angle, knuckle_diam=knuckle_diam, pin_diam=pin_diam,clip=clip,
|
||||
fill=fill, clear_top=clear_top, round_bot=round_bot, round_top=round_top, pin_fn=pin_fn,clearance=clearance,tearspin=tearspin);
|
||||
if (is_str(pin_diam)) back(clearance)right(offset) up(length/2-(inner?1:1)*z_adjust) zrot(default(tearspin,0)){
|
||||
|
||||
if (in_place!=false){
|
||||
angle = is_bool(in_place) ? 45 : in_place;
|
||||
doflip = inner?yflip():IDENT;
|
||||
len = (inner?seglen2:seglen1)-gap;
|
||||
cone_h = pin_diam/2*tan(angle);
|
||||
tag("keep")back(clearance)right(offset)
|
||||
rotate_extrude() polygon(apply(doflip,[
|
||||
[0,-len/2 + ((!inner && segs%2==1 && $idx==0) || ($idx==numsegs-1 && inner && segs%2==0) ? 0:cone_h)],
|
||||
[pin_diam/2, -len/2],
|
||||
[pin_diam/2+.01, -len/2],
|
||||
[pin_diam/2+.01, len/2],
|
||||
[pin_diam/2, len/2],
|
||||
if ($idx==numsegs-1 && !inner) [0,len/2]
|
||||
else each [
|
||||
[pin_diam*.1, len/2+cone_h*.8],
|
||||
[0,len/2+cone_h*.8]
|
||||
]
|
||||
]));
|
||||
}
|
||||
}
|
||||
if (!in_place && is_str(pin_diam)) back(clearance)right(offset) up(length/2-(inner?1:1)*z_adjust) zrot(default(tearspin,0)){
|
||||
$fn = default(pin_fn,$fn);
|
||||
tap_depth = min(segs%2==1?seglen1-gap/2:seglen2-gap/2, default(tap_depth, length));
|
||||
screw_hole(pin_diam, length=length+.01, tolerance="self tap", bevel=false, anchor=TOP, teardrop=is_def(tearspin));
|
||||
|
|
@ -311,7 +437,8 @@ module knuckle_hinge(length, segs, offset, inner=false, arm_height=0, arm_angle=
|
|||
}
|
||||
|
||||
|
||||
module _knuckle_hinge_profile(offset, arm_height, arm_angle=45, knuckle_diam=4, pin_diam=1.75, fill=true, clear_top=false, round_bot=0, round_top=0, pin_fn, clearance=0, tearspin)
|
||||
module _knuckle_hinge_profile(offset, arm_height, arm_angle=45, knuckle_diam=4, pin_diam=1.75, fill=true, clear_top=false,
|
||||
round_bot=0, round_top=0, pin_fn, clearance=0, tearspin, clip)
|
||||
{
|
||||
extra = .01;
|
||||
skel = turtle(["left", 90-arm_angle, "untilx", offset+extra, "left", arm_angle,
|
||||
|
|
@ -329,6 +456,7 @@ module _knuckle_hinge_profile(offset, arm_height, arm_angle=45, knuckle_diam=4,
|
|||
}
|
||||
if (clear_top==true || clear_top=="all") left(.1)fwd(clearance) rect([offset+knuckle_diam,knuckle_diam+1+clearance],anchor=BOT+LEFT);
|
||||
if (is_num(clear_top)) left(.1)fwd(clearance) rect([.1+clear_top, knuckle_diam+1+clearance], anchor=BOT+LEFT);
|
||||
if (is_def(clip)) fwd(clip) left(.1) rect([offset+knuckle_diam, ofs+round_bot+knuckle_diam+abs(clip)],anchor=BACK+LEFT);
|
||||
}
|
||||
right(offset)ellipse(d=knuckle_diam,realign=true,circum=true);
|
||||
}
|
||||
|
|
|
|||
96
paths.scad
96
paths.scad
|
|
@ -16,17 +16,22 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Section: Utility Functions
|
||||
// Definitions:
|
||||
// Point|Points = A list of numbers, also called a vector. Usually has length 2 or 3 to represent points in the place on points in space.
|
||||
// Pointlist|Pointlists|Point List|Point Lists = An unordered list of {{points}}.
|
||||
// Path|Paths = An ordered list of two or more {{points}} specifying a path through space. Usually points are 2D.
|
||||
// Polygon|Polygons = A {{path}}, usually 2D, that describes a polygon by asuming that the first and last point are connected.
|
||||
|
||||
// Function: is_path()
|
||||
// Synopsis: Returns True if 'list' is a path.
|
||||
// Synopsis: Returns True if 'list' is a {{path}}.
|
||||
// Topics: Paths
|
||||
// See Also: is_region(), is_vnf()
|
||||
// Usage:
|
||||
// is_path(list, [dim], [fast])
|
||||
// Description:
|
||||
// Returns true if `list` is a path. A path is a list of two or more numeric vectors (AKA points).
|
||||
// Returns true if `list` is a {{path}}. A path is a list of two or more numeric vectors (AKA {{points}}).
|
||||
// All vectors must of the same size, and may only contain numbers that are not inf or nan.
|
||||
// By default the vectors in a path must be 2d or 3d. Set the `dim` parameter to specify a list
|
||||
// By default the vectors in a path must be 2D or 3D. Set the `dim` parameter to specify a list
|
||||
// of allowed dimensions, or set it to `undef` to allow any dimension. (Note that this function
|
||||
// returns `false` on 1-regions.)
|
||||
// Example:
|
||||
|
|
@ -58,13 +63,13 @@ function is_path(list, dim=[2,3], fast=false) =
|
|||
&& (is_undef(dim) || in_list(len(list[0]), force_list(dim)));
|
||||
|
||||
// Function: is_1region()
|
||||
// Synopsis: Returns true if path is a region with one component.
|
||||
// Synopsis: Returns true if {{path}} is a {{region}} with one component.
|
||||
// Topics: Paths, Regions
|
||||
// See Also: force_path()
|
||||
// Usage:
|
||||
// bool = is_1region(path, [name])
|
||||
// Description:
|
||||
// If `path` is a region with one component (a 1-region) then return true. If path is a region with more components
|
||||
// If `path` is a {{region}} with one component (a single-{{path}} region, or 1-region) then returns true. If path is a region with more components
|
||||
// then display an error message about the parameter `name` requiring a path or a single component region. If the input
|
||||
// is not a region then return false. This function helps path functions accept 1-regions.
|
||||
// Arguments:
|
||||
|
|
@ -84,8 +89,8 @@ function is_1region(path, name="path") =
|
|||
// Usage:
|
||||
// outpath = force_path(path, [name])
|
||||
// Description:
|
||||
// If `path` is a region with one component (a 1-region) then returns that component as a path.
|
||||
// If path is a region with more components then displays an error message about the parameter
|
||||
// If `path` is a {{region}} with one component (a single-{{path}} region, or 1-region) then returns that component as a path.
|
||||
// If `path` is a region with more components then displays an error message about the parameter
|
||||
// `name` requiring a path or a single component region. If the input is not a region then
|
||||
// returns the input without any checks. This function helps path functions accept 1-regions.
|
||||
// Arguments:
|
||||
|
|
@ -134,7 +139,7 @@ function _path_select(path, s1, u1, s2, u2, closed=false) =
|
|||
// SynTags: Path
|
||||
// Topics: Paths, Regions
|
||||
// Description:
|
||||
// Takes a path and removes unnecessary sequential collinear points. Note that when `closed=true` either of the path
|
||||
// Takes a {{path}} and removes unnecessary sequential collinear {{points}}. Note that when `closed=true` either of the path
|
||||
// endpoints may be removed.
|
||||
// Usage:
|
||||
// path_merge_collinear(path, [eps])
|
||||
|
|
@ -168,7 +173,7 @@ function path_merge_collinear(path, closed, eps=EPSILON) =
|
|||
// Usage:
|
||||
// path_length(path,[closed])
|
||||
// Description:
|
||||
// Returns the length of the path.
|
||||
// Returns the length of the given {{path}}.
|
||||
// Arguments:
|
||||
// path = Path of any dimension or 1-region.
|
||||
// closed = true if the path is closed. Default: false
|
||||
|
|
@ -185,7 +190,7 @@ function path_length(path,closed) =
|
|||
|
||||
|
||||
// Function: path_segment_lengths()
|
||||
// Synopsis: Returns a list of the lengths of segments in a path.
|
||||
// Synopsis: Returns a list of the lengths of segments in a {{path}}.
|
||||
// Topics: Paths
|
||||
// See Also: path_length(), path_length_fractions()
|
||||
// Usage:
|
||||
|
|
@ -213,7 +218,7 @@ function path_segment_lengths(path, closed) =
|
|||
// Usage:
|
||||
// fracs = path_length_fractions(path, [closed]);
|
||||
// Description:
|
||||
// Returns the distance fraction of each point in the path along the path, so the first
|
||||
// Returns the distance fraction of each point in the {{path}} along the path, so the first
|
||||
// point is zero and the final point is 1. If the path is closed the length of the output
|
||||
// will have one extra point because of the final connecting segment that connects the last
|
||||
// point of the path to the first point.
|
||||
|
|
@ -241,7 +246,7 @@ function path_length_fractions(path, closed) =
|
|||
/// Usage:
|
||||
/// isects = _path_self_intersections(path, [closed], [eps]);
|
||||
/// Description:
|
||||
/// Locates all self intersection points of the given path. Returns a list of intersections, where
|
||||
/// Locates all self intersection {{points}} of the given {{path}}. Returns a list of intersections, where
|
||||
/// each intersection is a list like [POINT, SEGNUM1, PROPORTION1, SEGNUM2, PROPORTION2] where
|
||||
/// POINT is the coordinates of the intersection point, SEGNUMs are the integer indices of the
|
||||
/// intersecting segments along the path, and the PROPORTIONS are the 0.0 to 1.0 proportions
|
||||
|
|
@ -324,10 +329,10 @@ function _sum_preserving_round(data, index=0) =
|
|||
// Usage:
|
||||
// newpath = subdivide_path(path, n|refine=|maxlen=, [method=], [closed=], [exact=]);
|
||||
// Description:
|
||||
// Takes a path as input (closed or open) and subdivides the path to produce a more
|
||||
// Takes a {{path}} as input (closed or open) and subdivides the path to produce a more
|
||||
// finely sampled path. You control the subdivision process by using the `maxlen` arg
|
||||
// to specify a maximum segment length, or by specifying `n` or `refine`, which request
|
||||
// a certain point count in the output.
|
||||
// a certain {{point}} count in the output.
|
||||
// .
|
||||
// You can specify the point count using the `n` option, where
|
||||
// you give the number of points you want in the output, or you can use
|
||||
|
|
@ -470,8 +475,8 @@ function subdivide_path(path, n, refine, maxlen, closed=true, exact, method) =
|
|||
// Usage:
|
||||
// newpath = resample_path(path, n|spacing=, [closed=]);
|
||||
// Description:
|
||||
// Compute a uniform resampling of the input path. If you specify `n` then the output path will have n
|
||||
// points spaced uniformly (by linear interpolation along the input path segments). The only points of the
|
||||
// Compute a uniform resampling of the input {{path}}. If you specify `n` then the output path will have n
|
||||
// {{points}} spaced uniformly (by linear interpolation along the input path segments). The only points of the
|
||||
// input path that are guaranteed to appear in the output path are the starting and ending points, and any
|
||||
// points that have an angular deflection of at least the number of degrees given in `keep_corners`.
|
||||
// If you specify `spacing` then the length you give will be rounded to the nearest spacing that gives
|
||||
|
|
@ -561,14 +566,14 @@ function resample_path(path, n, spacing, keep_corners, closed=true) =
|
|||
// Section: Path Geometry
|
||||
|
||||
// Function: is_path_simple()
|
||||
// Synopsis: Returns true if a path has no self intersections.
|
||||
// Synopsis: Returns true if a {{path}} has no self intersections.
|
||||
// Topics: Paths
|
||||
// See Also: is_path()
|
||||
// Usage:
|
||||
// bool = is_path_simple(path, [closed], [eps]);
|
||||
// Description:
|
||||
// Returns true if the given 2D path is simple, meaning that it has no self-intersections.
|
||||
// Repeated points are not considered self-intersections: a path with such points can
|
||||
// Returns true if the given 2D {{path}} is simple, meaning that it has no self-intersections.
|
||||
// Repeated {{points}} are not considered self-intersections: a path with such points can
|
||||
// still be simple.
|
||||
// If closed is set to true then treat the path as a polygon.
|
||||
// Arguments:
|
||||
|
|
@ -597,13 +602,13 @@ function is_path_simple(path, closed, eps=EPSILON) =
|
|||
|
||||
|
||||
// Function: path_closest_point()
|
||||
// Synopsis: Returns the closest place on a path to a given point.
|
||||
// Synopsis: Returns the closest place on a {{path}} to a given {{point}}.
|
||||
// Topics: Paths
|
||||
// See Also: point_line_distance(), line_closest_point()
|
||||
// Usage:
|
||||
// index_pt = path_closest_point(path, pt);
|
||||
// Description:
|
||||
// Finds the closest path segment, and point on that segment to the given point.
|
||||
// Finds the closest {{path}} segment, and {{point}} on that segment to the given point.
|
||||
// Returns `[SEGNUM, POINT]`
|
||||
// Arguments:
|
||||
// path = Path of any dimension or a 1-region.
|
||||
|
|
@ -635,7 +640,7 @@ function path_closest_point(path, pt, closed=true) =
|
|||
// Usage:
|
||||
// tangs = path_tangents(path, [closed], [uniform]);
|
||||
// Description:
|
||||
// Compute the tangent vector to the input path. The derivative approximation is described in deriv().
|
||||
// Compute the tangent vector to the input {{path}}. The derivative approximation is described in deriv().
|
||||
// The returns vectors will be normalized to length 1. If any derivatives are zero then
|
||||
// the function fails with an error. If you set `uniform` to false then the sampling is
|
||||
// assumed to be non-uniform and the derivative is computed with adjustments to produce corrected
|
||||
|
|
@ -674,15 +679,15 @@ function path_tangents(path, closed, uniform=true) =
|
|||
// Usage:
|
||||
// norms = path_normals(path, [tangents], [closed]);
|
||||
// Description:
|
||||
// Compute the normal vector to the input path. This vector is perpendicular to the
|
||||
// Compute the normal vector to the input {{path}}. This vector is perpendicular to the
|
||||
// path tangent and lies in the plane of the curve. For 3d paths we define the plane of the curve
|
||||
// at path point i to be the plane defined by point i and its two neighbors. At the endpoints of open paths
|
||||
// at path {{point}} i to be the plane defined by point i and its two neighbors. At the endpoints of open paths
|
||||
// we use the three end points. For 3d paths the computed normal is the one lying in this plane that points
|
||||
// towards the center of curvature at that path point. For 2d paths, which lie in the xy plane, the normal
|
||||
// towards the center of curvature at that path point. For 2D paths, which lie in the xy plane, the normal
|
||||
// is the path pointing to the right of the direction the path is traveling. If points are collinear then
|
||||
// a 3d path has no center of curvature, and hence the
|
||||
// normal is not uniquely defined. In this case the function issues an error.
|
||||
// For 2d paths the plane is always defined so the normal fails to exist only
|
||||
// For 2D paths the plane is always defined so the normal fails to exist only
|
||||
// when the derivative is zero (in the case of repeated points).
|
||||
// Arguments:
|
||||
// path = 2D or 3D path or a 1-region
|
||||
|
|
@ -713,13 +718,13 @@ function path_normals(path, tangents, closed) =
|
|||
|
||||
|
||||
// Function: path_curvature()
|
||||
// Synopsis: Returns the estimated numerical curvature of the path.
|
||||
// Synopsis: Returns the estimated numerical curvature of the {{path}}.
|
||||
// Topics: Paths
|
||||
// See Also: path_tangents(), path_normals(), path_torsion()
|
||||
// Usage:
|
||||
// curvs = path_curvature(path, [closed]);
|
||||
// Description:
|
||||
// Numerically estimate the curvature of the path (in any dimension).
|
||||
// Numerically estimate the curvature of the {{path}} (in any dimension).
|
||||
// Arguments:
|
||||
// path = path in any dimension or a 1-region
|
||||
// closed = if true then treat the path as a polygon. Default: false
|
||||
|
|
@ -741,13 +746,13 @@ function path_curvature(path, closed) =
|
|||
|
||||
|
||||
// Function: path_torsion()
|
||||
// Synopsis: Returns the estimated numerical torsion of the path.
|
||||
// Synopsis: Returns the estimated numerical torsion of the {{path}}.
|
||||
// Topics: Paths
|
||||
// See Also: path_tangents(), path_normals(), path_curvature()
|
||||
// Usage:
|
||||
// torsions = path_torsion(path, [closed]);
|
||||
// Description:
|
||||
// Numerically estimate the torsion of a 3d path.
|
||||
// Numerically estimate the torsion of a 3d {{path}}.
|
||||
// Arguments:
|
||||
// path = 3D path
|
||||
// closed = if true then treat path as a polygon. Default: false
|
||||
|
|
@ -766,16 +771,16 @@ function path_torsion(path, closed=false) =
|
|||
|
||||
|
||||
// Function: surface_normals()
|
||||
// Synopsis: Estimates the normals to a surface defined by a point array
|
||||
// Synopsis: Estimates the normals to a surface defined by a {{point}} array
|
||||
// Topics: Math, Geometry
|
||||
// See Also: path_tangents(), path_normals()
|
||||
// Usage:
|
||||
// normals = surface_normals(surf, [col_wrap=], [row_wrap=]);
|
||||
// Description:
|
||||
// Numerically estimate the normals to a surface defined by a 2d array of 3d points, which can
|
||||
// also be regarded as an array of paths (all of the same length).
|
||||
// Numerically estimate the normals to a surface defined by a 2D array of 3d {{points}}, which can
|
||||
// also be regarded as an array of {{paths}} (all of the same length).
|
||||
// Arguments:
|
||||
// surf = surface in 3d defined by a 2d array of points
|
||||
// surf = surface in 3d defined by a 2D array of points
|
||||
// ---
|
||||
// row_wrap = if true then wrap path in the row direction (first index)
|
||||
// col_wrap = if true then wrap path in the column direction (second index)
|
||||
|
|
@ -796,19 +801,19 @@ function surface_normals(surf, col_wrap=false, row_wrap=false) =
|
|||
|
||||
|
||||
// Function: path_cut()
|
||||
// Synopsis: Cuts a path into subpaths at various points.
|
||||
// Synopsis: Cuts a {{path}} into subpaths at various {{points}}.
|
||||
// SynTags: PathList
|
||||
// Topics: Paths, Path Subdivision
|
||||
// See Also: split_path_at_self_crossings(), path_cut_points()
|
||||
// Usage:
|
||||
// path_list = path_cut(path, cutdist, [closed]);
|
||||
// Description:
|
||||
// Given a list of distances in `cutdist`, cut the path into
|
||||
// Given a list of distances in `cutdist`, cut the {{path}} into
|
||||
// subpaths at those lengths, returning a list of paths.
|
||||
// If the input path is closed then the final path will include the
|
||||
// original starting point. The list of cut distances must be
|
||||
// original starting {{point}}. The list of cut distances must be
|
||||
// in ascending order and should not include the endpoints: 0
|
||||
// or len(path). If you repeat a distance you will get an
|
||||
// or `len(path)`. If you repeat a distance you will get an
|
||||
// empty list in that position in the output. If you give an
|
||||
// empty cutdist array you will get the input path as output
|
||||
// (without the final vertex doubled in the case of a closed path).
|
||||
|
|
@ -858,14 +863,14 @@ function _path_cut_getpaths(path, cutlist, closed) =
|
|||
|
||||
|
||||
// Function: path_cut_points()
|
||||
// Synopsis: Returns a list of cut points at a list of distances from the first point in a path.
|
||||
// Synopsis: Returns a list of cut {{points}} at a list of distances from the first point in a {{path}}.
|
||||
// Topics: Paths, Path Subdivision
|
||||
// See Also: path_cut(), split_path_at_self_crossings()
|
||||
// Usage:
|
||||
// cuts = path_cut_points(path, cutdist, [closed=], [direction=]);
|
||||
//
|
||||
// Description:
|
||||
// Cuts a path at a list of distances from the first point in the path. Returns a list of the cut
|
||||
// Cuts a {{path}} at a list of distances from the first {{point}} in the path. Returns a list of the cut
|
||||
// points and indices of the next point in the path after that point. So for example, a return
|
||||
// value entry of [[2,3], 5] means that the cut point was [2,3] and the next point on the path after
|
||||
// this point is path[5]. If the path is too short then path_cut_points returns undef. If you set
|
||||
|
|
@ -996,14 +1001,14 @@ function _cut_to_seg_u_form(pathcut, path, closed) =
|
|||
|
||||
|
||||
// Function: split_path_at_self_crossings()
|
||||
// Synopsis: Split a 2D path wherever it crosses itself.
|
||||
// Synopsis: Split a 2D {{path}} wherever it crosses itself.
|
||||
// SynTags: PathList
|
||||
// Topics: Paths, Path Subdivision
|
||||
// See Also: path_cut(), path_cut_points()
|
||||
// Usage:
|
||||
// paths = split_path_at_self_crossings(path, [closed], [eps]);
|
||||
// Description:
|
||||
// Splits a 2D path into sub-paths wherever the original path crosses itself.
|
||||
// Splits a 2D {{path}} into sub-paths wherever the original path crosses itself.
|
||||
// Splits may occur mid-segment, so new vertices will be created at the intersection points.
|
||||
// Returns a list of the resulting subpaths.
|
||||
// Arguments:
|
||||
|
|
@ -1067,14 +1072,14 @@ function _tag_self_crossing_subpaths(path, nonzero, closed=true, eps=EPSILON) =
|
|||
|
||||
|
||||
// Function: polygon_parts()
|
||||
// Synopsis: Parses a self-intersecting polygon into a list of non-intersecting polygons.
|
||||
// Synopsis: Parses a self-intersecting polygon into a list of non-intersecting {{polygons}}.
|
||||
// SynTags: PathList
|
||||
// Topics: Paths, Polygons
|
||||
// See Also: split_path_at_self_crossings(), path_cut(), path_cut_points()
|
||||
// Usage:
|
||||
// splitpolys = polygon_parts(poly, [nonzero], [eps]);
|
||||
// Description:
|
||||
// Given a possibly self-intersecting 2d polygon, constructs a representation of the original polygon as a list of
|
||||
// Given a possibly self-intersecting 2D {{polygon}}, constructs a representation of the original polygon as a list of
|
||||
// non-intersecting simple polygons. If nonzero is set to true then it uses the nonzero method for defining polygon membership.
|
||||
// For simple cases, such as the pentagram, this will produce the outer perimeter of a self-intersecting polygon.
|
||||
// Arguments:
|
||||
|
|
@ -1327,7 +1332,4 @@ function _assemble_partial_paths_recur(edges, eps, paths=[], i=0) =
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
|||
134
regions.scad
134
regions.scad
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
|
||||
// Section: Regions
|
||||
// A region is a list of polygons meeting these conditions:
|
||||
// A {{region}} is a list of polygons meeting these conditions:
|
||||
// .
|
||||
// - Every polygon on the list is simple, meaning it does not intersect itself
|
||||
// - Two polygons on the list do not cross each other
|
||||
|
|
@ -37,32 +37,56 @@
|
|||
// Checking that a list of polygons is a valid region, meaning that it satisfies all of the conditions
|
||||
// above, can be a time consuming test, so it is not done automatically. It is your responsibility to ensure that your regions are
|
||||
// compliant. You can construct regions by making a suitable list of polygons, or by using
|
||||
// set operation function such as union() or difference(), which all acccept polygons, as
|
||||
// set operation function such as {{union()}} or {{difference()}}, which all acccept polygons, as
|
||||
// well as regions, as their inputs. If you must, you can clean up an ill-formed region using
|
||||
// {{make_region()}}, which breaks up self-intersecting polygons and polygons that cross each other.
|
||||
// {{make_region()}}, which breaks up self-intersecting polygons and polygons that cross each other.
|
||||
//
|
||||
// Figure(2D): An Sample {{Region}}
|
||||
// path1 = union([
|
||||
// circle(d=100),
|
||||
// translate([50,20], p=circle(d=40))
|
||||
// ])[0];
|
||||
// rgn = [
|
||||
// // Main shape
|
||||
// select(path1, hull(path1)),
|
||||
// translate([50,20], p=circle(d=20)),
|
||||
// rot(-8, p=hexagon(d=60, rounding=10)),
|
||||
// // Inner disjointed shape
|
||||
// circle(d=25),
|
||||
// rot(30-8, p=rect(10)),
|
||||
// // External disjoined shape
|
||||
// each translate([60,-25], p=rot(55, p=[
|
||||
// rect([20,15], rounding=5),
|
||||
// ellipse([6,3])
|
||||
// ])),
|
||||
// ];
|
||||
// region(rgn);
|
||||
//
|
||||
|
||||
// Definitions:
|
||||
// Region|Regions = A list of one or more non-intersecting {{polygons}} representing a union of one or more disconnected polygons that may have internal holes.
|
||||
|
||||
// Function: is_region()
|
||||
// Synopsis: Returns true if the input appears to be a region.
|
||||
// Synopsis: Returns true if the input appears to be a {{region}}.
|
||||
// Topics: Regions, Paths, Polygons, List Handling
|
||||
// See Also: is_valid_region(), is_1region(), is_region_simple()
|
||||
// Usage:
|
||||
// bool = is_region(x);
|
||||
// Description:
|
||||
// Returns true if the given item looks like a region. A region is a list of non-crossing simple polygons. This test just checks
|
||||
// Returns true if the given item looks like a {{region}}. A region is a list of non-crossing simple {{polygons}}. This test just checks
|
||||
// that the argument is a list whose first entry is a path.
|
||||
function is_region(x) = is_list(x) && is_path(x.x);
|
||||
|
||||
|
||||
// Function: is_valid_region()
|
||||
// Synopsis: Returns true if the input is a valid region.
|
||||
// Synopsis: Returns true if the input is a valid {{region}}.
|
||||
// Topics: Regions, Paths, Polygons, List Handling
|
||||
// See Also: is_region(), is_1region(), is_region_simple()
|
||||
// Usage:
|
||||
// bool = is_valid_region(region, [eps]);
|
||||
// Description:
|
||||
// Returns true if the input is a valid region, meaning that it is a list of simple polygons whose segments do not cross each other.
|
||||
// This test can be time consuming with regions that contain many points.
|
||||
// Returns true if the input is a valid {{region}}, meaning that it is a list of simple {{polygons}} whose segments do not cross each other.
|
||||
// This test can be time consuming with regions that contain many {{points}}.
|
||||
// It differs from `is_region()`, which simply checks that the object is a list whose first entry is a path
|
||||
// because it searches all the list polygons for any self-intersections or intersections with each other.
|
||||
// Also returns true if given a single simple polygon. Use {{make_region()}} to convert sets of self-intersecting polygons into
|
||||
|
|
@ -220,7 +244,7 @@ function _polygon_crosses_region(region, poly, eps=EPSILON) =
|
|||
// Usage:
|
||||
// bool = is_region_simple(region, [eps]);
|
||||
// Description:
|
||||
// We extend the notion of the simple path to regions: a simple region is entirely
|
||||
// We extend the notion of the simple {{path}} to {{regions}}: a simple region is entirely
|
||||
// non-self-intersecting, meaning that it is formed from a list of simple polygons that
|
||||
// don't intersect each other at all—not even with corner contact points.
|
||||
// Regions with corner contact are valid but may fail CGAL. Simple regions
|
||||
|
|
@ -247,7 +271,7 @@ function is_region_simple(region, eps=EPSILON) =
|
|||
|
||||
|
||||
// Function: make_region()
|
||||
// Synopsis: Converts lists of intersecting polygons into valid regions.
|
||||
// Synopsis: Converts lists of intersecting {{polygons}} into valid {{regions}}.
|
||||
// SynTags: Region
|
||||
// Topics: Regions, Paths, Polygons, List Handling
|
||||
// See Also: force_region(), region()
|
||||
|
|
@ -255,8 +279,8 @@ function is_region_simple(region, eps=EPSILON) =
|
|||
// Usage:
|
||||
// region = make_region(polys, [nonzero], [eps]);
|
||||
// Description:
|
||||
// Takes a list of polygons that may intersect themselves or cross each other
|
||||
// and converts it into a properly defined region without these defects.
|
||||
// Takes a list of {{polygons}} that may intersect themselves or cross each other
|
||||
// and converts it into a properly defined {{region}} without these defects.
|
||||
// Arguments:
|
||||
// polys = list of polygons to use
|
||||
// nonzero = set to true to use nonzero rule for polygon membership. Default: false
|
||||
|
|
@ -297,14 +321,14 @@ function force_region(poly) = is_path(poly) ? [poly] : poly;
|
|||
// Section: Turning a region into geometry
|
||||
|
||||
// Module: region()
|
||||
// Synopsis: Creates the 2D polygons described by the given region or list of polygons.
|
||||
// Synopsis: Creates the 2D {{polygons}} described by the given {{region}} or list of polygons.
|
||||
// SynTags: Geom
|
||||
// Topics: Regions, Paths, Polygons, List Handling
|
||||
// See Also: make_region(), debug_region()
|
||||
// Usage:
|
||||
// region(r, [anchor], [spin=], [cp=], [atype=]) [ATTACHMENTS];
|
||||
// Description:
|
||||
// Creates the 2D polygons described by the given region or list of polygons. This module works on
|
||||
// Creates the 2D {{polygons}} described by the given {{region}} or list of polygons. This module works on
|
||||
// arbitrary lists of polygons that cross each other and hence do not define a valid region. The
|
||||
// displayed result is the exclusive-or of the polygons listed in the input.
|
||||
// Arguments:
|
||||
|
|
@ -345,7 +369,7 @@ module region(r, anchor="origin", spin=0, cp="centroid", atype="hull")
|
|||
|
||||
|
||||
// Module: debug_region()
|
||||
// Synopsis: Draws an annotated region.
|
||||
// Synopsis: Draws an annotated {{region}}.
|
||||
// SynTags: Geom
|
||||
// Topics: Shapes (2D)
|
||||
// See Also: region(), debug_polygon(), debug_vnf(), debug_bezier()
|
||||
|
|
@ -353,7 +377,7 @@ module region(r, anchor="origin", spin=0, cp="centroid", atype="hull")
|
|||
// Usage:
|
||||
// debug_region(region, [vertices=], [edges=], [convexity=], [size=]);
|
||||
// Description:
|
||||
// A replacement for {{region()}} that displays the region and labels the vertices and
|
||||
// A replacement for {{region()}} that displays the {{region}} and labels the vertices and
|
||||
// edges. The region vertices and edges are labeled with letters to identify the path
|
||||
// component in the region, starting with A.
|
||||
// The start of each path is marked with a blue circle and the end with a pink diamond.
|
||||
|
|
@ -392,13 +416,13 @@ module debug_region(region, vertices=true, edges=true, convexity=2, size=1)
|
|||
// Section: Geometrical calculations with regions
|
||||
|
||||
// Function: point_in_region()
|
||||
// Synopsis: Tests if a point is inside, outside, or on the border of a region.
|
||||
// Synopsis: Tests if a point is inside, outside, or on the border of a {{region}}.
|
||||
// Topics: Regions, Points, Comparison
|
||||
// See Also: region_area(), are_regions_equal()
|
||||
// Usage:
|
||||
// check = point_in_region(point, region, [eps]);
|
||||
// Description:
|
||||
// Tests if a point is inside, outside, or on the border of a region.
|
||||
// Tests if a point is inside, outside, or on the border of a {{region}}.
|
||||
// Returns -1 if the point is outside the region.
|
||||
// Returns 0 if the point is on the boundary.
|
||||
// Returns 1 if the point lies inside the region.
|
||||
|
|
@ -430,12 +454,12 @@ function _point_in_region(point, region, eps=EPSILON, i=0, cnt=0) =
|
|||
|
||||
|
||||
// Function: region_area()
|
||||
// Synopsis: Computes the area of the specified valid region.
|
||||
// Synopsis: Computes the area of the specified valid {{region}}.
|
||||
// Topics: Regions, Area
|
||||
// Usage:
|
||||
// area = region_area(region);
|
||||
// Description:
|
||||
// Computes the area of the specified valid region. (If the region is invalid and has self intersections
|
||||
// Computes the area of the specified valid {{region}}. (If the region is invalid and has self intersections
|
||||
// the result is meaningless.)
|
||||
// Arguments:
|
||||
// region = region whose area to compute
|
||||
|
|
@ -561,15 +585,15 @@ function _region_region_intersections(region1, region2, closed1=true,closed2=tru
|
|||
|
||||
|
||||
// Function: split_region_at_region_crossings()
|
||||
// Synopsis: Splits regions where polygons touch and at intersections.
|
||||
// Synopsis: Splits {{regions}} where {{polygons}} touch and at intersections.
|
||||
// Topics: Regions, Polygons, List Handling
|
||||
// See Also: region_parts()
|
||||
//
|
||||
// Usage:
|
||||
// split_region = split_region_at_region_crossings(region1, region2, [closed1], [closed2], [eps])
|
||||
// Description:
|
||||
// Splits region1 at the places where polygons in region1 touches each other at corners and at locations
|
||||
// where region1 intersections region2. Split region2 similarly with respect to region1.
|
||||
// Splits the {{region} `region1` at the places where its {{polygons}} touches each other at corners and at locations
|
||||
// where `region1` intersects `region2`. Split `region2` similarly with respect to `region1`.
|
||||
// The return is a pair of results of the form [split1, split2] where split1=[frags1,frags2,...]
|
||||
// and frags1 is a list of paths that when placed end to end (in the given order), give the first polygon of region1.
|
||||
// Each path in the list is either entirely inside or entirely outside region2.
|
||||
|
|
@ -628,14 +652,14 @@ function split_region_at_region_crossings(region1, region2, closed1=true, closed
|
|||
|
||||
|
||||
// Function: region_parts()
|
||||
// Synopsis: Splits a region into a list of connected regions.
|
||||
// Synopsis: Splits a {{region}} into a list of connected regions.
|
||||
// SynTags: RegList
|
||||
// Topics: Regions, List Handling
|
||||
// See Also: split_region_at_region_crossings()
|
||||
// Usage:
|
||||
// rgns = region_parts(region);
|
||||
// Description:
|
||||
// Divides a region into a list of connected regions. Each connected region has exactly one clockwise outside boundary
|
||||
// Divides a {{region}} into a list of connected regions. Each connected region has exactly one clockwise outside boundary
|
||||
// and zero or more counter-clockwise outlines defining internal holes. Behavior is undefined on invalid regions whose
|
||||
// components cross each other.
|
||||
// Example(2D,NoAxes):
|
||||
|
|
@ -804,14 +828,14 @@ function _point_dist(path,pathseg_unit,pathseg_len,pt) =
|
|||
|
||||
|
||||
// Function: offset()
|
||||
// Synopsis: Takes a 2D path, polygon or region and returns a path offset by an amount.
|
||||
// Synopsis: Takes a 2D {{path}}, {{polygon}} or {{region}} and returns a path offset by an amount.
|
||||
// SynTags: Path, Region, Ext
|
||||
// Topics: Paths, Polygons, Regions
|
||||
// Usage:
|
||||
// offsetpath = offset(path, [r=|delta=], [chamfer=], [closed=], [check_valid=], [quality=], [error=], [same_length=])
|
||||
// path_faces = offset(path, return_faces=true, [r=|delta=], [chamfer=], [closed=], [check_valid=], [quality=], [error=], [firstface_index=], [flip_faces=])
|
||||
// Description:
|
||||
// Takes a 2D input path, polygon or region and returns a path offset by the specified amount. As with the built-in
|
||||
// Takes a 2D input {{path}}, {{polygon}} or {{region}} and returns a path offset by the specified amount. As with the built-in
|
||||
// offset() module, you can use `r` to specify rounded offset and `delta` to specify offset with
|
||||
// corners. If you used `delta` you can set `chamfer` to true to get chamfers.
|
||||
// When `closed=true` (the default), the input is treated as a polygon. If the input is a region it is treated as a collection
|
||||
|
|
@ -1200,7 +1224,7 @@ function _list_three(a,b,c) =
|
|||
// region = union(REGION1,REGION2);
|
||||
// region = union(REGION1,REGION2,REGION3);
|
||||
// Description:
|
||||
// When called as a function and given a list of regions or 2D polygons,
|
||||
// When called as a function and given a list of {{regions}} or 2D {{polygons}},
|
||||
// returns the union of all given regions and polygons. Result is a single region.
|
||||
// When called as the built-in module, makes the union of the given children.
|
||||
// This function is **much** slower than the native union module acting on geometry,
|
||||
|
|
@ -1237,7 +1261,7 @@ function union(regions=[],b=undef,c=undef,eps=EPSILON) =
|
|||
// region = difference(REGION1,REGION2);
|
||||
// region = difference(REGION1,REGION2,REGION3);
|
||||
// Description:
|
||||
// When called as a function, and given a list of regions or 2D polygons,
|
||||
// When called as a function, and given a list of {{regions}} or 2D {{polygons}},
|
||||
// takes the first region or polygon and differences away all other regions/polygons from it. The resulting
|
||||
// region is returned.
|
||||
// When called as the built-in module, makes the set difference of the given children.
|
||||
|
|
@ -1276,7 +1300,7 @@ function difference(regions=[],b=undef,c=undef,eps=EPSILON) =
|
|||
// region = intersection(REGION1,REGION2);
|
||||
// region = intersection(REGION1,REGION2,REGION3);
|
||||
// Description:
|
||||
// When called as a function, and given a list of regions or polygons returns the
|
||||
// When called as a function, and given a list of {{regions}} or {{polygons}} returns the
|
||||
// intersection of all given regions. Result is a single region.
|
||||
// When called as the built-in module, makes the intersection of all the given children.
|
||||
// Arguments:
|
||||
|
|
@ -1312,7 +1336,7 @@ function intersection(regions=[],b=undef,c=undef,eps=EPSILON) =
|
|||
// region = exclusive_or(REGION1,REGION2);
|
||||
// region = exclusive_or(REGION1,REGION2,REGION3);
|
||||
// Description:
|
||||
// When called as a function and given a list of regions or 2D polygons,
|
||||
// When called as a function and given a list of {{regions}} or 2D {{polygons}},
|
||||
// returns the exclusive_or of all given regions. Result is a single region.
|
||||
// When called as a module, performs a Boolean exclusive-or of up to 10 children. Note that when
|
||||
// the input regions cross each other, the exclusive-or operator produces shapes that
|
||||
|
|
@ -1466,14 +1490,14 @@ module exclusive_or() {
|
|||
|
||||
|
||||
// Function&Module: hull_region()
|
||||
// Synopsis: Compute convex hull of region or 2d path
|
||||
// Synopsis: Compute convex hull of {{region}} or 2D {{path}}
|
||||
// SynTags: Geom, Path
|
||||
// Topics: Regions, Polygons, Shapes2D
|
||||
// Usage:
|
||||
// path = hull_region(region);
|
||||
// hull_region(region);
|
||||
// Description:
|
||||
// Given a path, or a region, compute the convex hull
|
||||
// Given a {{path}}, or a {{region}}, compute the convex hull
|
||||
// and return it as a path. This differs from {{hull()}} and {{hull2d_path()}}, which
|
||||
// return an index list into the point list. As a module invokes the native hull() on
|
||||
// the specified region.
|
||||
|
|
@ -1499,4 +1523,48 @@ module hull_region(region)
|
|||
}
|
||||
|
||||
|
||||
// Function: fill()
|
||||
// Synopsis: Remove holes from a {{region}}
|
||||
// SynTags: Geom, Path
|
||||
// Topics: Regions, Polygons, Shapes2D
|
||||
// Usage:
|
||||
// filled = fill(region);
|
||||
// Description:
|
||||
// Given a {{region}}, fill in any internal holes in the components of the region. This returns the outside border of each region component, and
|
||||
// is equivalent to {{hull()}} for region components whose outside boundary is convex.
|
||||
// Arguments:
|
||||
// region = region to fill
|
||||
// Example(2D, NoAxes): The original region in green has internal holes and subparts that are all filled in on the yellow filled region.
|
||||
// $fs=.5;$fa=1;
|
||||
// reg=[circle(r=10),right(5.5,circle(r=1)),
|
||||
// circle(r=8), circle(r=3),
|
||||
// right(5.5,rect([3,4]))];
|
||||
// color("green")region(reg);
|
||||
// right(25)region(fill(reg));
|
||||
// Example(2D, NoAxes): Here the input region in green has two components:
|
||||
// reg = [circle(8), circle(6)];
|
||||
// reg2 = union([reg, fwd(18,reg)]);
|
||||
// color("green")region(reg2);
|
||||
// right(18) region(fill(reg2));
|
||||
|
||||
function fill(region) =
|
||||
let(
|
||||
region = force_region(region)
|
||||
)
|
||||
assert(is_region(region), "\nInput is not a region.")
|
||||
let(
|
||||
inside = [for(i=idx(region))
|
||||
let(pt = mean([region[i][0], region[i][1]]))
|
||||
[for(j=idx(region)) i==j ? 0
|
||||
: point_in_polygon(pt,region[j]) >=0 ? 1 : 0]
|
||||
],
|
||||
level = inside*repeat(1,len(region))
|
||||
)
|
||||
[ for(i=idx(region))
|
||||
if(level[i]==0) clockwise_polygon(region[i])];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@
|
|||
// two prisms together with a rounded fillet at the joint.
|
||||
// Includes:
|
||||
// include <BOSL2/std.scad>
|
||||
// include <BOSL2/rounding.scad>
|
||||
// FileGroup: Advanced Modeling
|
||||
// FileSummary: Round path corners, rounded prisms, rounded cutouts in tubes, filleted prism joints
|
||||
// FileFootnotes: STD=Included in std.scad
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
include <structs.scad>
|
||||
|
||||
// Section: Types of Roundovers
|
||||
// The functions and modules in this file support two different types of roundovers and some different mechanisms for specifying
|
||||
|
|
@ -2877,7 +2876,6 @@ function _circle_mask(r) =
|
|||
function bent_cutout_mask(r, thickness, path, radius, convexity=10) = no_function("bent_cutout_mask");
|
||||
module bent_cutout_mask(r, thickness, path, radius, convexity=10)
|
||||
{
|
||||
no_children($children);
|
||||
r = get_radius(r1=r, r2=radius);
|
||||
dummy1=assert(is_def(r) && r>0,"Radius of the cylinder to bend around must be positive");
|
||||
path2 = force_path(path);
|
||||
|
|
@ -2896,7 +2894,7 @@ module bent_cutout_mask(r, thickness, path, radius, convexity=10)
|
|||
innerzero = repeat([0,0,zmean], len(fixpath));
|
||||
outerpt = repeat( [1.5*mindist*cos((maxangle+minangle)/2),1.5*mindist*sin((maxangle+minangle)/2),zmean], len(fixpath));
|
||||
default_tag("remove")
|
||||
vnf_polyhedron(vnf_vertex_array([innerzero, each profiles, outerpt],col_wrap=true),convexity=convexity);
|
||||
vnf_polyhedron(vnf_vertex_array([innerzero, each profiles, outerpt],col_wrap=true),convexity=convexity) children();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
123
skin.scad
123
skin.scad
|
|
@ -1731,7 +1731,15 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
|
|||
// You can also apply scaling to the profile along the path. You can give a list of scalar scale factors or a list of 2-vector scale.
|
||||
// In the latter scale the x and y scales of the profile are scaled separately before the profile is placed onto the path. For non-closed
|
||||
// paths you can also give a single scale value or a 2-vector, which is treated as the final scale. The intermediate sections
|
||||
// are then scaled by linear interpolation either relative to length (if scale_by_length is true) or by point count otherwise.
|
||||
// are then scaled by linear interpolation either relative to length (if scale_by_length is true) or by point count otherwise.
|
||||
// .
|
||||
// The `caps` parameter controls what happens at the ends of the polyhedron. If `closed=true` the shape links to itself and has no
|
||||
// ends, but when `closed` is false, the two ends are, by default capped with flat faces. If you set `caps=false` then the ends
|
||||
// receive no faces and the resulting non-manifold polyhedron has exposed edges. You can also set caps to a number, which adds a
|
||||
// rounded cap with the specified radius, or you can set caps to an {{offset_sweep()}} end treatment, and the specified sweep will
|
||||
// be attached as a cap. Note that you are **adding** a rounded cap, not rounding the specified shape as is common for many other
|
||||
// library modules. The rounded cap is attached to the end face and may not blend neatly with the swept shape unless the sides of
|
||||
// the swept shape are perpendicular to the end cap.
|
||||
// .
|
||||
// You can use set `transforms` to true to return a list of transformation matrices instead of the swept shape. In this case, you can
|
||||
// often omit shape entirely. The exception is when `closed=true` and you are using the "incremental" method. In this case, `path_sweep`
|
||||
|
|
@ -1764,7 +1772,7 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
|
|||
// uniform = if set to false then compute tangents using the uniform=false argument, which may give better results when your path is non-uniformly sampled. This argument is passed to {{path_tangents()}}. Default: true
|
||||
// tangent = a list of tangent vectors in case you need more accuracy (particularly at the end points of your curve)
|
||||
// relaxed = set to true with the "manual" method to relax the orthogonality requirement of cross sections to the path tangent. Default: false
|
||||
// caps = Can be a boolean or vector of two booleans. Set to false to disable caps at the two ends. Default: true
|
||||
// caps = if closed is false, set caps to false to leave the ends open. Other values are true to create a flat cap, a number a rounded cap, or an {{offset_sweep()}} end treatment to create the specified offset sweep. Can be a single value or pair of values to control the caps independently at each end. Default: true
|
||||
// style = vnf_vertex_array style. Default: "min_edge"
|
||||
// profiles = if true then display all the cross section profiles instead of the solid shape. Can help debug a sweep. (module only) Default: false
|
||||
// width = the width of lines used for profile display. (module only) Default: 1
|
||||
|
|
@ -2101,6 +2109,15 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
|
|||
// closed=true, twist=360*2/5,symmetry=5,
|
||||
// texture="bricks_vnf",tex_reps=[10,40],
|
||||
// tex_depth=.1);
|
||||
// Example(NoScales): Applying rounded end caps to a sweep
|
||||
// $fs=1;$fa=1;
|
||||
// path_sweep(circle(r=5), arc(r=15, angle=[0,230]),caps=2.5);
|
||||
// Example(NoScales): Using a small `$fn` creates a chamfer on the endcap
|
||||
// $fs=1;$fa=1;
|
||||
// path_sweep(circle(r=5), arc(r=15, angle=[0,230]),caps=1, $fn=4);
|
||||
// Example(NoScales): One flat endcap and one rounding with a negative radius
|
||||
// $fs=1;$fa=1;
|
||||
// path_sweep(circle(r=5), arc(r=15, angle=[180,330]),caps=[true, -3]);
|
||||
|
||||
|
||||
module path_sweep(shape, path, method="incremental", normal, closed, twist=0, twist_by_length=true, scale=1, scale_by_length=true,
|
||||
|
|
@ -2113,9 +2130,6 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw
|
|||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
|
||||
trans_scale = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, scale, scale_by_length,
|
||||
symmetry, last_normal, tangent, uniform, relaxed, caps, style, transforms=true,_return_scales=true);
|
||||
caps = is_def(caps) ? caps :
|
||||
closed ? false : true;
|
||||
fullcaps = is_bool(caps) ? [caps,caps] : caps;
|
||||
transforms = trans_scale[0];
|
||||
scales = trans_scale[1];
|
||||
firstscale = is_num(scales[0]) ? 1/scales[0] : [1/scales[0].x, 1/scales[0].y];
|
||||
|
|
@ -2125,7 +2139,7 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw
|
|||
shape_normals = -path3d(path_normals(clockwise_polygon(shape), closed=true))
|
||||
)
|
||||
[for(T=transforms) apply(_force_rot(T),shape_normals)];
|
||||
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, _closed_for_normals=closed, caps=fullcaps,style=style,
|
||||
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, _closed_for_normals=closed, caps=caps,style=style,
|
||||
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples, normals=tex_normals,
|
||||
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip);
|
||||
shapecent = point3d(centroid(shape));
|
||||
|
|
@ -2181,10 +2195,6 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
|
|||
assert((is_region(shape) || is_path(shape,2)) || (transforms && !(closed && method=="incremental")),"shape must be a 2d path or region")
|
||||
let(
|
||||
path = path3d(path),
|
||||
caps = is_def(caps) ? caps :
|
||||
closed ? false : true,
|
||||
capsOK = is_bool(caps) || is_bool_list(caps,2),
|
||||
fullcaps = is_bool(caps) ? [caps,caps] : caps,
|
||||
normalOK = is_undef(normal) || (method!="natural" && is_vector(normal,3))
|
||||
|| (method=="manual" && same_shape(normal,path)),
|
||||
scaleOK = scale==1 || ((is_num(scale) || is_vector(scale,2)) && !closed) || is_vector(scale,len(path)) || is_matrix(scale,len(path),2)
|
||||
|
|
@ -2193,8 +2203,6 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
|
|||
assert(normalOK, method=="natural" ? "Cannot specify normal with the \"natural\" method"
|
||||
: method=="incremental" ? "Normal with \"incremental\" method must be a 3-vector"
|
||||
: str("Incompatible normal given. Must be a 3-vector or a list of ",len(path)," 3-vectors"))
|
||||
assert(capsOK, "caps must be boolean or a list of two booleans")
|
||||
assert(!closed || !caps, "Cannot make closed shape with caps")
|
||||
assert(is_undef(normal) || (is_vector(normal) && len(normal)==3) || (is_path(normal) && len(normal)==len(path) && len(normal[0])==3), "Invalid normal specified")
|
||||
assert(is_undef(tangent) || (is_path(tangent) && len(tangent)==len(path) && len(tangent[0])==3), "Invalid tangent specified")
|
||||
assert(scaleOK,str("Incompatible or invalid scale",closed?" for closed path":"",": must be ", closed?"":"a scalar, a 2-vector, ",
|
||||
|
|
@ -2330,7 +2338,7 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
|
|||
transforms && _return_scales
|
||||
? [transform_list,scale]
|
||||
: transforms ? transform_list
|
||||
: sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=fullcaps,style=style,
|
||||
: sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=caps,style=style,
|
||||
anchor=anchor,cp=cp,spin=spin,orient=orient,atype=atype,
|
||||
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples,
|
||||
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip,
|
||||
|
|
@ -2485,9 +2493,16 @@ function _ofs_face_edge(face,firstlen,second=false) =
|
|||
// is a list of 4x4 transformation matrices. The sweep algorithm applies each transformation in sequence
|
||||
// to the shape input and links the resulting polygons together to form a polyhedron.
|
||||
// If `closed=true` then the first and last transformation are linked together.
|
||||
// The `caps` parameter controls whether the ends of the shape are closed.
|
||||
// As a function, returns the VNF for the polyhedron. As a module, computes the polyhedron.
|
||||
// .
|
||||
// The `caps` parameter controls what happens at the ends of the polyhedron. If `closed=true` the shape links to itself and has no
|
||||
// ends, but when `closed` is false, the two ends are, by default capped with flat faces. If you set `caps=false` then the ends
|
||||
// receive no faces and the resulting non-manifold polyhedron has exposed edges. You can also set caps to a number, which adds a
|
||||
// rounded cap with the specified radius, or you can set caps to an {{offset_sweep()}} end treatment, and the specified sweep will
|
||||
// be attached as a cap. Note that you are **adding** a rounded cap, not rounding the specified shape as is common for many other
|
||||
// library modules. The rounded cap is attached to the end face and may not blend neatly with the swept shape unless the sides of
|
||||
// the swept shape are perpendicular to the end cap.
|
||||
// .
|
||||
// This is a powerful, general framework for producing polyhedra. It is important
|
||||
// to ensure that your resulting polyhedron does not include any self-intersections, or it will
|
||||
// be invalid and generate CGAL errors. If you get such errors, most likely you have an
|
||||
|
|
@ -2500,11 +2515,13 @@ function _ofs_face_edge(face,firstlen,second=false) =
|
|||
// This works by passing through to {{vnf_vertex_array()}}, which also has more details on
|
||||
// texturing. Note that textures work only when the shape is a path; you cannot apply a texture to a region.
|
||||
// The texture tiles are oriented on the path sweep so that the Y axis of the tile is aligned with the sweep direction.
|
||||
// .
|
||||
//
|
||||
// Arguments:
|
||||
// shape = 2d path or region, describing the shape to be swept.
|
||||
// transforms = list of 4x4 matrices to apply
|
||||
// closed = set to true to form a closed (torus) model. Default: false
|
||||
// caps = true to create endcap faces when closed is false. Can be a singe boolean to specify endcaps at both ends, or a length 2 boolean array. Default is true if closed is false.
|
||||
// caps = if closed is false, set caps to false to leave the ends open. Other values are true to create a flat cap, a number a rounded cap, or an {{offset_sweep()}} end treatment to create the specified offset sweep. Can be a single value or pair of values to control the caps independently at each end. Default: true
|
||||
// style = vnf_vertex_array style. Default: "min_edge"
|
||||
// ---
|
||||
// convexity = convexity setting for use with polyhedron. (module only) Default: 10
|
||||
|
|
@ -2570,15 +2587,24 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
|
|||
assert(is_consistent(transforms, ident(4)), "Input transforms must be a list of numeric 4x4 matrices in sweep")
|
||||
assert(is_path(shape,2) || is_region(shape), "Input shape must be a 2d path or a region.")
|
||||
let(
|
||||
caps = is_def(caps) ? caps :
|
||||
closed ? false : true,
|
||||
capsOK = is_bool(caps) || is_bool_list(caps,2),
|
||||
fullcaps = is_bool(caps) ? [caps,caps] : caps
|
||||
caps = is_list(caps) && select(caps,0,1)==["for","offset_sweep"] ? [caps,caps]
|
||||
: is_bool(caps) || is_num(caps) ? [caps,caps]
|
||||
: is_undef(caps) ? closed ? [false,false] : [true,true]
|
||||
: caps,
|
||||
capsOK = is_list(caps) && len(caps)==2
|
||||
&&
|
||||
[] == [for(cap=caps)
|
||||
if (!(is_bool(cap) || is_num(cap) || select(cap,0,1)==["for","offset_sweep"])) 1],
|
||||
flatcaps = [for(cap=caps) is_bool(cap) ? cap : false],
|
||||
fancycaps = [for(cap=caps) is_bool(cap) ? false
|
||||
: is_num(cap) ? os_circle(r=cap,steps=ceil(segs(cap)/4))
|
||||
: cap]
|
||||
)
|
||||
assert(len(transforms)>=2, "transformation must be length 2 or more")
|
||||
assert(capsOK, "caps must be boolean or a list of two booleans")
|
||||
assert(!closed || !caps, "Cannot make closed shape with caps")
|
||||
assert(capsOK, "caps must be boolean, number, an offset_sweep specification, or a list of two of those")
|
||||
assert(!closed || caps==[false,false], "Cannot make closed shape with caps")
|
||||
is_region(shape)?
|
||||
assert(fancycaps==[false,false], "rounded caps are not supported for regions")
|
||||
assert(is_undef(texture), "textures are not supported for regions, only paths")
|
||||
let(
|
||||
regions = region_parts(shape),
|
||||
|
|
@ -2587,8 +2613,8 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
|
|||
for (rgn=regions) each [
|
||||
for (path=rgn)
|
||||
sweep(path, transforms, closed=closed, caps=false, style=style),
|
||||
if (fullcaps[0]) vnf_from_region(rgn, transform=transforms[0], reverse=true),
|
||||
if (fullcaps[1]) vnf_from_region(rgn, transform=last(transforms)),
|
||||
if (flatcaps[0]) vnf_from_region(rgn, transform=transforms[0], reverse=true),
|
||||
if (flatcaps[1]) vnf_from_region(rgn, transform=last(transforms)),
|
||||
],
|
||||
],
|
||||
vnf = vnf_join(vnfs)
|
||||
|
|
@ -2603,12 +2629,22 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
|
|||
: let(
|
||||
n = surface_normals(select(points,0,-2), col_wrap=true, row_wrap=true)
|
||||
)
|
||||
[each n, n[0]]
|
||||
)
|
||||
vnf_vertex_array(points, normals=normals,
|
||||
cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style,
|
||||
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples,
|
||||
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip);
|
||||
[each n, n[0]],
|
||||
vva_result = vnf_vertex_array(points, normals=normals,
|
||||
cap1=flatcaps[0],cap2=flatcaps[1],col_wrap=true,style=style, return_edges=fancycaps!=[false,false],
|
||||
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples,
|
||||
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip),
|
||||
vnf = fancycaps==[false,false] ? vva_result
|
||||
: vnf_join(
|
||||
[ vva_result[0],
|
||||
for(ind=[0,1])
|
||||
if (fancycaps[ind]) let(
|
||||
polygon = vva_result[1][ind+2],
|
||||
plane = plane_from_polygon(ind==0? reverse(polygon) : polygon)
|
||||
)
|
||||
apply(lift_plane(plane),offset_sweep(project_plane(plane, polygon), top=fancycaps[ind], caps=[false,true]))
|
||||
])
|
||||
) vnf;
|
||||
|
||||
|
||||
module sweep(shape, transforms, closed=false, caps, style="min_edge", convexity=10,
|
||||
|
|
@ -4952,7 +4988,7 @@ module _textured_revolution(
|
|||
}
|
||||
|
||||
|
||||
function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0, triangulate=false, tex_scaling="default",
|
||||
function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0, triangulate=false, tex_scaling="default",return_edges=false,
|
||||
col_wrap=false, tex_depth=1, row_wrap=false, caps, cap1, cap2, reverse=false, style="min_edge", tex_extra, tex_skip, sidecaps,sidecap1,sidecap2,normals) =
|
||||
assert(tex_reps==undef || is_int(tex_reps) || (all_integer(tex_reps) && len(tex_reps)==2), "tex_reps must be an integer or list of two integers")
|
||||
assert(tex_size==undef || is_num(tex_size) || is_vector(tex_size,2), "tex_size must be a scalar or 2-vector")
|
||||
|
|
@ -5016,7 +5052,8 @@ function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples,
|
|||
]
|
||||
]
|
||||
)
|
||||
vnf_vertex_array(tex_surf, row_wrap=row_wrap, col_wrap=col_wrap, reverse=reverse,style=style, caps=caps, cap1=cap1, cap2=cap2, triangulate=triangulate)
|
||||
vnf_vertex_array(tex_surf, row_wrap=row_wrap, col_wrap=col_wrap, reverse=reverse,style=style,
|
||||
caps=caps, cap1=cap1, cap2=cap2, triangulate=triangulate, return_edges=return_edges)
|
||||
: // VNF case
|
||||
let(
|
||||
local_scale = [for(y=[-1:1:ptsize.y])
|
||||
|
|
@ -5068,7 +5105,7 @@ function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples,
|
|||
)
|
||||
base + _tex_height(tex_depth,tex_inset,pt.z) * normal*(reverse?-1:1) * scale,
|
||||
fullvnf = vnf_join([
|
||||
for(y=[0:1:tex_reps.y-1], x=[0:1:tex_reps.x-1])
|
||||
for(y=[0:1:tex_reps.y-1], x=[0:1:tex_reps.x-1]) // Main body of the textured shape
|
||||
[
|
||||
[for(pt=vnf[0]) trans_pt(x,y,pt)],
|
||||
vnf[1]
|
||||
|
|
@ -5092,9 +5129,27 @@ function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples,
|
|||
[for(pt = closed_path) trans_pt(x,y,[x?1:0,pt.y,pt.z])]]
|
||||
)
|
||||
for(path=cap_paths) [path, [count(path,reverse=x!=0)]]
|
||||
])
|
||||
]),
|
||||
edgepaths = !return_edges ? undef
|
||||
: [
|
||||
if (!col_wrap)
|
||||
for(x=[0, tex_reps.x-1])
|
||||
[for(y=[0:1:tex_reps.y-1],pt=xedge_paths[0][0])
|
||||
trans_pt(x,y,[x?1:0,pt.y,pt.z])]
|
||||
else each [[],[]],
|
||||
|
||||
if (!row_wrap && len(yedge_paths[0])>0)
|
||||
for(ind=[0,1])
|
||||
if ([cap1,cap2][ind]) []
|
||||
else let(y=[0,tex_reps.y-1][ind])
|
||||
[for(x=[0:1:tex_reps.x-1], pt=yedge_paths[0][0])
|
||||
trans_pt(x,y,[pt.x,y?0:1,pt.z])]
|
||||
else each [[],[]]
|
||||
],
|
||||
revvnf = reverse ? vnf_reverse_faces(fullvnf) : fullvnf
|
||||
|
||||
)
|
||||
reverse ? vnf_reverse_faces(fullvnf) : fullvnf;
|
||||
!return_edges ? revvnf : [revvnf, edgepaths];
|
||||
|
||||
|
||||
// Resamples a point array to the specified size.
|
||||
|
|
|
|||
10
sliders.scad
10
sliders.scad
|
|
@ -35,15 +35,17 @@
|
|||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
|
||||
// $slop = The printer-specific slop value to make parts fit just right.
|
||||
// Example:
|
||||
// slider(l=30, base=10, wall=4, $slop=0.2, spin=90);
|
||||
// slider(l=30, base=10, wall=4, $slop=0.2);
|
||||
// Example(VPD=190;VPR=[75,0,350]): Vertically centered anchors are aligned with the slider V tips.
|
||||
// slider(l=30, base=10, wall=4) show_anchors();
|
||||
function slider(l=30, w=10, h=10, base=10, wall=5, ang=30, chamfer=2, anchor=BOTTOM, spin=0, orient=UP) = no_function("slider");
|
||||
module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, chamfer=2, anchor=BOTTOM, spin=0, orient=UP)
|
||||
{
|
||||
full_width = w + 2*wall;
|
||||
full_height = h + base;
|
||||
|
||||
attachable(anchor,spin,orient, size=[l, full_width, full_height], offset=[0,0,-h/2]) {
|
||||
zrot(90)
|
||||
attachable(anchor,spin,orient, size=[full_width, l, full_height], offset=[0,0,-h/2]) {
|
||||
zrot(0)
|
||||
down(base+h/2) {
|
||||
// Base
|
||||
cuboid([full_width, l, base-get_slop()], chamfer=chamfer, edges=[FRONT,BACK], except_edges=BOT, anchor=BOTTOM);
|
||||
|
|
@ -88,6 +90,8 @@ module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, chamfer=2, anchor=BOTTO
|
|||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
|
||||
// Example:
|
||||
// rail(l=100, w=10, h=10);
|
||||
// Example(VPD=325;VPR=[80,0,25]): Anchors
|
||||
// rail(l=100, w=10, h=10) show_anchors();
|
||||
function rail(l=30, w=10, h=10, chamfer=1.0, ang=30, anchor=BOTTOM, spin=0, orient=UP) = no_function("rail");
|
||||
module rail(l=30, w=10, h=10, chamfer=1.0, ang=30, anchor=BOTTOM, spin=0, orient=UP)
|
||||
{
|
||||
|
|
|
|||
3
std.scad
3
std.scad
|
|
@ -35,10 +35,11 @@ include <geometry.scad>
|
|||
include <regions.scad>
|
||||
include <strings.scad>
|
||||
include <vnf.scad>
|
||||
include <structs.scad>
|
||||
include <rounding.scad>
|
||||
include <skin.scad>
|
||||
include <utility.scad>
|
||||
include <partitions.scad>
|
||||
include <structs.scad>
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
||||
|
|
|
|||
26
vnf.scad
26
vnf.scad
|
|
@ -73,6 +73,12 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
|||
// For creating the texture, `vnf_vertex_array()` uses normals to the surface that it estimates from the surface data itself.
|
||||
// If you have more accurate normals or need the normals to take particular values, you can pass an array of normals
|
||||
// using the `normals` parameter.
|
||||
// .
|
||||
// You can set `return_edges=true` to return the paths of the four edges of the output. In this case the return value
|
||||
// is `[vnf,edgelist]` where edgelist is [left (column 0 of points), right (last column of points), top (points[0]), bottom (last(points)]. If a given
|
||||
// edge does not exist then it will be the empty list in the output. An edge only exists it is not capped and not wrapped. The main
|
||||
// need for this feature is when you have added a texture and need a way to interface the shape with something else. In this case you cannot
|
||||
// easily determine the edges yourself from the input point list. edges are not easily
|
||||
// Arguments:
|
||||
// points = A list of vertices to divide into columns and rows.
|
||||
// ---
|
||||
|
|
@ -99,6 +105,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
|||
// sidecap2 = set sidecap only for the `points[][max]` edge of the output
|
||||
// tex_scaling = set to "const" to disable grid size vertical scaling of the texture. Default: "default"
|
||||
// normals = array of normal vectors to each point in the point array for more accurate texture height calculation
|
||||
// return_edges = if true return [vnf,edgelist] where edgelist is the paths of four edges, [left (column 0 of points), right (last column of points), top (points[0]), bottom (last(points)]. Default: false
|
||||
// cp = (module) Centerpoint for determining intersection anchors or centering the shape. Determines the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
||||
// anchor = (module) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"`
|
||||
// spin = (module) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
|
|
@ -326,7 +333,7 @@ function vnf_vertex_array(
|
|||
row_wrap=false,
|
||||
reverse=false,
|
||||
style="default",
|
||||
triangulate = false,
|
||||
triangulate = false, return_edges=false,
|
||||
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0, tex_scaling="default",
|
||||
tex_depth=1, tex_extra, tex_skip, sidecaps,sidecap1,sidecap2, normals
|
||||
) =
|
||||
|
|
@ -336,7 +343,7 @@ function vnf_vertex_array(
|
|||
assert(is_bool(triangulate))
|
||||
is_def(texture) ?
|
||||
_textured_point_array(points=points, texture=texture, tex_reps=tex_reps, tex_size=tex_size,
|
||||
tex_inset=tex_inset, tex_samples=tex_samples, tex_rot=tex_rot, tex_scaling=tex_scaling,
|
||||
tex_inset=tex_inset, tex_samples=tex_samples, tex_rot=tex_rot, tex_scaling=tex_scaling, return_edges=return_edges,
|
||||
col_wrap=col_wrap, row_wrap=row_wrap, tex_depth=tex_depth, caps=caps, cap1=cap1, cap2=cap2, reverse=reverse,
|
||||
style=style, tex_extra=tex_extra, tex_skip=tex_skip, sidecaps=sidecaps, sidecap1=sidecap1, sidecap2=sidecap2,normals=normals,triangulate=triangulate)
|
||||
:
|
||||
|
|
@ -431,10 +438,17 @@ function vnf_vertex_array(
|
|||
)
|
||||
rfaces,
|
||||
],
|
||||
vnf = [verts, allfaces]
|
||||
) triangulate? vnf_triangulate(vnf) : vnf;
|
||||
|
||||
|
||||
vnf = [verts, allfaces],
|
||||
tvnf = triangulate? vnf_triangulate(vnf) : vnf
|
||||
)
|
||||
!return_edges ? tvnf
|
||||
: [tvnf, [
|
||||
if (!col_wrap) deduplicate(column(points,0)) else [],
|
||||
if (!col_wrap) deduplicate(column(points, len(points[0])-1)) else [],
|
||||
if (!cap1 && !row_wrap) deduplicate(points[0]) else [],
|
||||
if (!cap2 && !row_wrap) deduplicate(last(points)) else []
|
||||
]
|
||||
];
|
||||
|
||||
// Function&Module: vnf_tri_array()
|
||||
// Synopsis: Returns a VNF from an array of points. The array need not be rectangular.
|
||||
|
|
|
|||
Loading…
Reference in a new issue