[User]
I'd like to design a shaft collar that I can customize in a few different ways. I've seen some designs, and they look fairly straightforward, but I'd like to make sure I can adjust everything I need.

Essentially, I need a cylindrical collar that clamps down on a shaft. It needs a wedge-shaped cut-out to allow for tightening and a screw to do the clamping. It should have these adjustable parameters:
*   **Inner Diameter:** This is the diameter of the shaft that the collar will grip.
*   **Printer Adjustment:** A small amount to add to the inner diameter to compensate for printer tolerances.
*   **Opening Angle:** The angle of the wedge-shaped cut-out.
*   **Contact Face Offset:** A small gap between the two cut faces, to prevent binding.
*   **Screw Diameter:** The diameter of the screw that will be used to clamp the collar.
*   **Screw Head Diameter:** The diameter of the screw head.
*   **Screw Head Offset:**  The offset between the flat of the collar and the screw head.
*   **Nut Sides:** Either 4 for square or 6 for hex nuts.
*   **Axial Holes:** an option to either have 0 or 2 axial aligned holes.
*   **Axial Hole Diameter:** The diameter of the optional axial aligned holes.

I'd also like the collar to automatically generate a few other values like:
*   **Thickness:** The overall thickness of the collar.
*   **Outer Diameter:**  The outer diameter of the collar calculated from the inner diameter and thickness.

There should be a label on the flat side of the collar that shows the inner diameter, and also an inside chamfer (or similar) to assist grip. The cut for the wedge should be extruded, not rotated, if that makes sense. I want the screws to be placed on the outer radius, as shown in the examples provided. I would like this in a single file with all parameters clearly labeled, if possible.

I plan to print these on a Prusa i3 Mk3, and I often use M2-M4 screws, so those should be taken into account.

I'm not sure which libraries would be useful for this, if any.

[Assistant]
shaft-collar-2.scad:
```
// customizable shaft collar
// dan@marginallyclever.com 2018-11-23
// default measurements are in mm.

inner_diameter=5;  // [1:0.1:100]

// compensate for printer tolerance
printer_adjust = 0.05;  // [0:0.05:1]

// opening angle (degrees)
opening_angle=6;  // [1:0.5:10]

// extra space between contacting faces (mm)
offet_contact_faces=0.1;  // [0:0.05:1]

// (mm) do not include printer tolerance!
screw_diameter=3;

// (mm) do not include printer tolerance!
screw_head_diameter=6;

screw_head_offset=screw_head_diameter*2/3;

nut_sides=6;  // [4:square nut,6:hex nut]

// axially-aligned screw holes for mounting other things on the shaft
axial_holes = 0;  // [0,2]
axial_hole_diameter = 2;  // [2:1:10]

/* [Hidden] */
// collar thickness
thickness=screw_head_diameter+2;
outer_diameter=inner_diameter+thickness*2;
// make sure CSG edges are not touching by overlapping them this much.
overlap = 0.1;
// description on flat side of collar
label =str(inner_diameter);
// position halfway betwee inner and outer diameter
middle = (outer_diameter+inner_diameter)/4;

chamfer_size=0.0;
chamfer = inner_diameter*tan(opening_angle);

difference()
{
    cylinder(h=thickness,d=outer_diameter+printer_adjust,center=true, $fn=100);
    cylinder(h=thickness+overlap,d=inner_diameter+printer_adjust,center=true, $fn=100);

    // cut the wedge where the collar can tighten
    linear_extrude(height=thickness+0.1,center=true)
        polygon([[0,offet_contact_faces],
                 [outer_diameter,outer_diameter*tan(opening_angle)],
                 [outer_diameter,-outer_diameter*tan(opening_angle)],
                 [0,-offet_contact_faces]
                ]);

    // chamfer the inside corners
    translate([(inner_diameter)/2,0,0])
        cylinder(h=thickness+overlap,r=chamfer,center=true,$fn=100);

    // cut the hole for the screw
    translate([middle,0,0])
        rotate([90,0,0])
            linear_extrude(height=outer_diameter+overlap,center=true)
                circle(d=screw_diameter,$fn=100);

    // cut the hole for the screw head
    translate([middle,-screw_head_offset,0])
        rotate([90,0,0])
            linear_extrude(height=outer_diameter+0.1,center=false)
                circle(d=screw_head_diameter+printer_adjust,$fn=100);

    // cut the hole for the nut
    translate([middle,screw_head_offset,0])
        rotate([-90,0,0])
            linear_extrude(height=outer_diameter+overlap,center=false)
                circle(d=screw_head_diameter+printer_adjust,$fn=nut_sides);

    // label
    rotate([0,0,90])
        translate([0,middle,thickness/2-0.5])
            linear_extrude(height=0.6,center=false)
                text(text=label,size=5,halign="center",valign="center");

    // axially aligned screw holes
    if(axial_holes==2) {
        translate([0,middle,0])
            cylinder(h=thickness*2,d=axial_hole_diameter+printer_adjust,center=true, $fn=100);
        translate([0,-middle,0])
            cylinder(h=thickness*2,d=axial_hole_diameter+printer_adjust,center=true, $fn=100);
    }
}
```
shaft-collar-2.scad:
```
// customizable shaft collar
// dan@marginallyclever.com 2018-11-23
// default measurements are in mm.

inner_diameter=5;  // [1:0.1:100]

// compensate for printer tolerance
printer_adjust = 0.05;  // [0:0.05:1]

// opening angle (degrees)
opening_angle=6;  // [1:0.5:10]

// extra space between contacting faces (mm)
offet_contact_faces=0.1;  // [0:0.05:1]

// (mm) do not include printer tolerance!
screw_diameter=3;

// (mm) do not include printer tolerance!
screw_head_diameter=6;

screw_head_offset=screw_head_diameter*2/3;

nut_sides=6;  // [4:square nut,6:hex nut]

// axially-aligned screw holes for mounting other things on the shaft
axial_holes = 0;  // [0,2]
axial_hole_diameter = 2;  // [2:1:10]

/* [Hidden] */
// collar thickness
thickness=screw_head_diameter+2;
outer_diameter=inner_diameter+thickness*2;
// make sure CSG edges are not touching by overlapping them this much.
overlap = 0.1;
// description on flat side of collar
label =str(inner_diameter);
// position halfway betwee inner and outer diameter
middle = (outer_diameter+inner_diameter)/4;

chamfer_size=0.0;
chamfer = inner_diameter*tan(opening_angle);

difference()
{
    cylinder(h=thickness,d=outer_diameter+printer_adjust,center=true, $fn=100);
    cylinder(h=thickness+overlap,d=inner_diameter+printer_adjust,center=true, $fn=100);

    // cut the wedge where the collar can tighten
    linear_extrude(height=thickness+0.1,center=true)
        polygon([[0,offet_contact_faces],
                 [outer_diameter,outer_diameter*tan(opening_angle)],
                 [outer_diameter,-outer_diameter*tan(opening_angle)],
                 [0,-offet_contact_faces]
                ]);

    // chamfer the inside corners
    translate([(inner_diameter)/2,0,0])
        cylinder(h=thickness+overlap,r=chamfer,center=true,$fn=100);

    // cut the hole for the screw
    translate([middle,0,0])
        rotate([90,0,0])
            linear_extrude(height=outer_diameter+overlap,center=true)
                circle(d=screw_diameter,$fn=100);

    // cut the hole for the screw head
    translate([middle,-screw_head_offset,0])
        rotate([90,0,0])
            linear_extrude(height=outer_diameter+0.1,center=false)
                circle(d=screw_head_diameter+printer_adjust,$fn=100);

    // cut the hole for the nut
    translate([middle,screw_head_offset,0])
        rotate([-90,0,0])
            linear_extrude(height=outer_diameter+overlap,center=false)
                circle(d=screw_head_diameter+printer_adjust,$fn=nut_sides);

    // label
    rotate([0,0,90])
        translate([0,middle,thickness/2-0.5])
            linear_extrude(height=0.6,center=false)
                text(text=label,size=5,halign="center",valign="center");

    // axially aligned screw holes
    if(axial_holes==2) {
        translate([0,middle,0])
            cylinder(h=thickness*2,d=axial_hole_diameter+printer_adjust,center=true, $fn=100);
        translate([0,-middle,0])
            cylinder(h=thickness*2,d=axial_hole_diameter+printer_adjust,center=true, $fn=100);
    }
}
```


[User]
Okay, I'm looking to create a parametric cryptex lock, similar to the one I've seen in some movies, but with a focus on being hard to pick. I want to be able to change the number of dials, the characters on each dial, the diameter, and some other features. I have some initial ideas about how it would function, but I'd love an OpenSCAD design that allows me to tweak everything.

Here are the core features:

*   **Dials:** The lock will have several rotating dials. The number of these should be configurable.
*   **Characters:** Each dial will display a set of characters or numbers. The characters should be unique per dial, based on a string or input. The number of characters per dial is linked to the number of sides for the rings.
*   **Code:** The lock will have a specific combination that opens it. This combination should be derived from the character string in an ordered way, such as characters 0, `sides`, `2 * sides`, etc.
*   **Core:** There will be a core that holds the dials and has the necessary structures to interact with the springs.
*   **Springs:** The dials will be held in place by springs. These should be parametric and adjustable.
*   **Bumps:** I'd also want bumps to give a tactile click when a dial rotates to a new position.
*   **Tolerances:** It needs to have parametric tolerances, so that it can be adjusted to compensate for printing imperfections.
*   **Visuals:** I would like to have the text on the dials recessed into the surface for better readability after printing.

Specifically:

1.  I'd like a main `.scad` file that controls all of the parameters and calls the modules for each piece. I'm thinking the main parameters should be:
    *   `dial_count`: The number of dials on the lock.
    *   `sides`: The number of faces/characters on each dial.
    *   `dial_thickness`: The thickness of the rotating dial rings.
    *   `side_size`: The width of the dial face where the text is printed.
    *    `character_depth`: how far the letters are cut into the dial.
    *   `code_string`: The complete string of characters that the dials will use.
    *   `tolerance`: A general tolerance value for fitting parts.
    *   `layer_height`: Used for the spring design.
    *   `spring_gap`: Space between the springs and the dial.
    *   `spring_thickness`: How thick the spring pieces are.

2.  The output should allow for separate rendering of different components:
    *   The central core
    *   The springs
    *   Individual dials
    *   A full exploded view
    *   A complete assembly

3. The code should allow for the design to be easily modified for different character fonts. The characters should be generated on the center of each dial's face.

4. I am thinking that the lock opens when the characters at code positions are lined up.

I'm not too concerned about the particular method for creating the springs - I would just like the design to be functional and printable with a standard FDM printer.

I think that covers most of what I'm looking for - can you help me put this together? I don't need code snippets for libraries or anything; I just want the OpenSCAD files.

[Assistant]
cryptex.scad:
```
$fn=60+0;

part="explode";//[springs:springs,dial0:dial 1,dial1:dial 2,dial2:dial 3,dial3:dial 4,dial4:dial 5,dial5:dial 6,dial6:dial 7,dial7:dial 8,dial8:dial 9,dial9:dial 10,compartment:compartment,assembly:assembly,explode:explode]

//tollerance for loose fit
tollerance=0.3;//[0.0:0.01:0.5]
//Printing layer thick
layer_h=0.2;//[0.04:0.04:0.4]
//separation between the springs and the rest of the part
spring_gap=0.5;//[0.2:0.01:1]
//spring thickness. thinner is weaker, but may not print well. 0.8 works for 0.4 nozzle with Cura.
spring_thick=0.8;//[0.5:0.01:1]
//how many dials.
//the number of faces on the rings
sides=10;//[6:1:26]
//thickness of dials
dial_thick=10;//[10:1:20]
//width of dial face
sidesze=10;//[10:1:20]
//how deep the letters go
character_depth=0.5;//[0.2:0.05:0.5]
code_length=4;//[3:1:10]
//what to put on the dials.
code="K1XL32G40D0A5M8U6O7901M42VO53ALS6U8E7T90";

springh=dial_thick*2/3;
springw=0.65+0;

bumph=4*min(dial_thick,sidesze)/20;
bumpoff=180/sides*springw+atan2(-(bumph+spring_gap),(sides*sidesze)/PI/2);

diald=((sides*sidesze)/PI+bumph*6);

echo(bumph);
echo(diald);
echo(code_length*dial_thick);
echo(bumpoff);
printem();
module printem()
{
    if(part=="springs"){springcore();}
    if(part=="compartment"){core();}

    if(part[0]=="d"){
        for(i=[0:1:9])
    {
        if(str(i)==part[4]){
            a=i;
            digitdials(a);
        }
    }

    }

    if(part=="assembly"){alltogethernow();}
    if(part=="explode"){explode();}
}
module alltogethernow()
{
  core();
  translate([0,0,dial_thick*0.75+tollerance]){
    springcore();
      for(i=[0:1:code_length-1]){
          f=(i<=0);
    l=(i>=(code_length-1));
          translate([0,0,i*dial_thick+dial_thick*(i==0?0:0.25)-dial_thick/4])translate([0,0,dial_thick*(f||l?2.5:2)/2-2*tollerance])rotate([180,0,0])digitdials(i);
      }
  }
}

module explode()
{
  core();
  translate([0,0,dial_thick*(code_length+1.75)+tollerance]){
    translate([0,0,dial_thick*code_length*1.5])springcore();
      for(i=[0:1:code_length-1]){
          f=(i<=0);
    l=(i>=(code_length-1));
          translate([0,0,i*dial_thick+dial_thick*(i==0?0:0.25)-dial_thick/4+dial_thick/2*i])translate([0,0,dial_thick*(f||l?2.5:2)/2-2*tollerance])rotate([180,0,0])digitdials(i);
      }
  }
}

module digitdials(dial)
{
    a=dial;
    f=(a<=0);
    l=(a>=(code_length-1));
    if(a>=code_length)
        {
       linear_extrude(height=1)text("no dial here",size=sidesze*1.25,font="Arial:style=Bold",valign="center",halign="center");
    }
    else
    {
    translate([0,0,dial_thick*1.25-dial_thick*2.5/2])translate([0,0,dial_thick*(f||l?2.5:2)/2-2*tollerance])rotate([180,0,0])digit(code,a,f,l);
    }
}
module springcore()
{
    rotate([0,0,-bumpoff]){

        //core
    difference()
    {
    union()
        {
         rotate([0,0,bumpoff])translate([(sides*sidesze)/PI/2-bumph,0,dial_thick/8])cube([bumph*2-tollerance,bumph*2-tollerance,dial_thick/4],center=true);
    difference()
    {
        union(){
            cylinder(d=(sides*sidesze)/PI,h=dial_thick*code_length);
        translate([0,0,dial_thick*code_length])cylinder(d1=(sides*sidesze)/PI,d2=(sides*sidesze)/PI+dial_thick/2,h=dial_thick/4);

            translate([0,0,dial_thick*code_length+dial_thick/4])cylinder(d1=(sides*sidesze)/PI+dial_thick/2,d2=2,h=((sides*sidesze)/PI-2*bumph)/2+dial_thick/2);
            }
        translate([0,0,dial_thick*code_length+dial_thick/4])cylinder(d1=(sides*sidesze)/PI-2*bumph,d2=0,h=((sides*sidesze)/PI-2*bumph)/2);
        translate([0,0,-1])cylinder(d=(sides*sidesze)/PI-2*bumph,h=dial_thick*code_length+2+dial_thick/4-1);
    }
}
springcut();
}
    rotate([0,0,bumpoff])bumps();
}
}
module digitdial(first,last)
{
    translate([0,0,-(dial_thick*(first||last?2.5:2)/2)/2])difference()
    {
    translate([0,0,(dial_thick*(first||last?2.5:2)/2)/2])rotate([0,0,180/sides])cylinder(d=diald,h=dial_thick*(first||last?2.5:2)/2-tollerance,$fn=sides,center=true);

        translate([0,0,-1])cylinder(d=(sides*sidesze)/PI+2*tollerance,h=dial_thick*3/2);

        cylinder(d1=(sides*sidesze)/PI+dial_thick/4+4*tollerance,d2=(sides*sidesze)/PI+2*tollerance,h=dial_thick/8);

        translate([0,0,dial_thick*(first||last?1.25:1)])rotate([180,0,0])cylinder(d1=(sides*sidesze)/PI+dial_thick/4+4*tollerance,d2=(sides*sidesze)/PI+2*tollerance,h=dial_thick/8);
        if(first||last)
        {
            translate([0,0,-2*tollerance])cylinder(d1=(sides*sidesze)/PI+sidesze/2+4*tollerance,d2=(sides*sidesze)/PI+2*tollerance,h=dial_thick/3);
        }
    }
}
module digit(faces,dial,first,last)
{

        translate([0,0,(first?dial_thick/4:0)])
    {
    difference()
        {

        translate([0,0,(dial_thick*(first||last?2.5:2)/2-tollerance)/2-(first?dial_thick/4:0)])
    {
    if(last){
        rotate([180,0,0])digitdial(first,last);
    }
    else
    {
        digitdial(first,last);
    }

}
translate([0,0,(dial_thick*0.75-bumph)])rotate_extrude()translate([(sides*sidesze)/PI/2+tollerance,0])rotate(45)square((bumph/2)*sqrt(2),center=true);
dialnotches();
numgen(faces,dial);
}
}
}
module dialnotches()
{
    for(i=[360/sides:360/sides:359])rotate([0,0,i])translate([0,0,dial_thick*0.75-bumph])dialnotch();

}
module dialnotch()
{
    a=360/(((sides*sidesze)/PI/2+tollerance)*2*PI/bumph);
        hull()
        {
        rotate([0,0,-a/2])rotate_extrude(angle=a)translate([(sides*sidesze)/PI/2,0])rotate(45)square(bumph*sqrt(2),center=true);
    rotate([0,0,-a*2])rotate_extrude(angle=a*4)translate([(sides*sidesze)/PI/2,0])rotate(45)square((bumph/2-tollerance)*sqrt(2),center=true);
}
}
module numgen(faces,dial)
{
   for(i=[0:1:sides-1])
    {
        rotate([0,0,360/sides*i])
        {
            translate([(diald*sin(180/sides))/tan(180/sides)/2,0,dial_thick/2])
            {
                rotate([180,-90,0])
                {
                    linear_extrude(height=character_depth*2,center=true)
                    {
                        text(faces[dial*sides+(sides-i)%sides],size=min(dial_thick,sidesze)*0.75,font="Arial:style=Bold",valign="center",halign="center");
                    }
                }
            }
        }
    }

}
module core()
{
    translate([0,0,dial_thick*.75]){
    difference()
    {
    rotate([0,0,180/sides])translate([0,0,-dial_thick*0.75])cylinder(d=diald,h=dial_thick/2,$fn=sides);
    translate([(diald*sin(180/sides))/tan(180/sides)/2,0,-dial_thick/2-1])cube([1,sidesze/4,dial_thick/2+3],center=true);
}
    translate([0,0,-dial_thick/4])cylinder(d1=(sides*sidesze)/PI+sidesze/2,d2=(sides*sidesze)/PI,h=dial_thick/4);
//tower
    difference()
    {
    cylinder(d=(sides*sidesze)/PI-2*bumph-tollerance*2,h=dial_thick*code_length);
    dials();

            translate([(sides*sidesze)/PI/2-bumph,0,(dial_thick*code_length-tollerance)/2-1])cube([bumph*2+tollerance,bumph*2+tollerance,dial_thick*code_length-2*tollerance+3],center=true);

        translate([0,0,-1])cylinder(d=(sides*sidesze)/PI-4*bumph-tollerance-2,h=dial_thick*code_length-2*tollerance+3);

    }
}
}

module dials()
{
    for(d=[0:1:code_length-1])translate([0,0,d*dial_thick+(dial_thick*0.75-springh/4)])rotate_extrude()translate([((sides*sidesze)/PI-2*bumph-0.2)/2,0])rotate(45)square(bumph/2*sqrt(2),center=true);
}

module bumps()
{

    for(d=[0:1:code_length-1])
    {

    translate([0,0,d*dial_thick])for(i=[0:360/sides:359])rotate([0,0,i])
        translate([(sides*sidesze)/PI/2-spring_thick/2,0,dial_thick*0.75-bumph])
    {

        rotate_extrude()polygon([[spring_thick/2,-bumph],[0,-bumph],[0,bumph],[spring_thick/2,bumph],[bumph,0]]);
    }
}
}

module springcuta()
{

    translate([0,0,springh-springh/3])cube([bumph*3,sidesze*springw-spring_gap/2,springh*2/3+0.001],center=true);

}
module springcutb()
{

    translate([spring_thick,0,springh])linear_extrude(height=(sidesze/2)*sqrt(2)/2,scale=[1,0.01],slices=10)square([bumph*2,sidesze*springw+spring_gap],center=true);

        translate([spring_thick,(sidesze*springw-spring_gap)/2,springh-springh/3])cube([bumph*2,spring_gap,springh*2/3+layer_h],center=true);

        translate([spring_thick,0,springh/3])cube([bumph*2,sidesze*springw,layer_h],center=true);

}
module springcut()
{
    intersection()
        {
            cylinder(r=(sides*sidesze)/PI/2-spring_thick,h=dial_thick*code_length);
    for(d=[0:1:code_length-1])
    {

    translate([0,0,d*dial_thick+dial_thick/4])for(i=[0:360/sides:359])rotate([0,0,i])    translate([(sides*sidesze)/PI/2-spring_thick,0,-springh/4])
    {
       render()springcuta();
    }
            }
}
for(d=[0:1:code_length-1])
    {

    translate([0,0,d*dial_thick+dial_thick/4])for(i=[0:360/sides:359])rotate([0,0,i])    translate([(sides*sidesze)/PI/2-spring_thick-bumph/2,0,-springh/4])
    {
        render()springcutb();
    }
}
}
```


[User]
I'd like to create a program that can generate a variety of knot-like shapes based on spirograph-like curves, but in 3D. I have an example SCAD file which does exactly this, but it would be great to start fresh with a clean version that is perhaps more modular. I'd like the code to be able to create a path using one of several types of curves, then use that path to create a tube, the result being a 3D knot.

Here are the specific requirements I'd like you to use.

First, I need the following curve types, selectable by an integer, likely starting with 0:
*   **Hypotrochoid:** A circle rolling inside another circle, with a point offset from the center.
*   **Epitrochoid:** A circle rolling outside another circle, with a point offset from the center.
*   **Rhodonea curve:** A rose-like curve.
*   **Epicycloid:** A special case of the epitrochoid where the point is on the edge of the rolling circle.
*   **Hypocycloid:** A special case of the hypotrochoid where the point is on the edge of the rolling circle.

Each of these curves will need several parameters, but I'm thinking it's best to use the following unified set:
*   `Ring_Radius`:  Radius of the base circle.
*   `Circle_Radius`: Radius of the rolling circle.
*   `Circle_Distance`:  Distance of the tracing point from the center of the rolling circle.
*   `Angle_Multiple`:  A multiplier for the angle when creating the z-coordinate (adds a twist).
*   `Angle_Divisor`: A divisor for the angle when creating the z-coordinate.

The resulting path should be scaled in the x, y, and z directions by parameters `vx`, `vy`, and `vz` respectively.

For the 3D knot, I want to create a tube along the calculated path. The tube should have a circular cross-section with a `Rope_Radius`. The resolution of this cross-section is set by `Sides`, which is the number of segments in the circle. The smoothness of the path is controlled by `Step`, which controls the angle increment.

There should be a parameter `Open` that determines if the knot is open or closed. A partial knot is created if the `Open` parameter is true and should utilize the `Start` and `End` parameters to set the start and end of the path.

The shape of the tube should also rotate along the path, controlled by a `Phase` parameter, making some interesting shapes for small values of `Sides`.

Finally, I would like the result to be a single `polyhedron`, which I believe will require me to precalculate all the vertices and faces. So you'll need to have separate functions for calculating these.

Let me know if you have any questions. I'm excited to see what you come up with!

[Assistant]
Spirograph_Knot.scad:
```
/*
    remix of "Celtic knots as Lissajous knots"
    by Kit Wallace
    https://www.thingiverse.com/thing:2257185

    and curve definitions from "Customizable curve generator"
    by kakaroto
    https://www.thingiverse.com/thing:50249

    to create Spirograph (and other) Knots
*/
function GCD(a,b) = (a==0)?b:GCD(b%a,a);
function LCM(a,b) = a*b/GCD(a,b);

shape=0;// [ 0:circle on inside = Hypotrochoid, 1:circle on outside = Epitrochoid, 2:Rhodonea_curve, 3:Epicycloid, 4:Hypocycloid ]

// radius of ring
Ring_Radius=7; // [1:30]
// radius of moving circle
Circle_Radius=3; // [1:30]
// distance of pen from circle center
Circle_Distance=7; // [1:30]
// multiple of angle to draw z
Angle_Multiple=2; // [1:5]
// divisor of angle to draw z
Angle_Divisor=1; // [1:5]

// Sides of rope - must be a divisor of 360
Sides=4; // [3,4,5,6,8,9,10,12,15,30,45,60]
// decrease for finer details
Step = 6; // [0.8:0.2:10]
// x scaling
vx = 3.0; // [1.0:0.5:20]
// y scaling
vy = 3.0; // [1.0:0.5:20]
// z scaling
vz = 10.0; // [2:60]
// Rope diameter
Rope_Radius=1.0; // [0.1:0.1:5.0]
// true if knot is open or partial
Open = false; // [false,true]

// phase angle for profile (matters for low Sides)
Phase = 45; // [0:5:180]

/* [Hidden] */
// change for partial path
Start=0; // [0:360]
// change for partial path
End=360*LCM(Ring_Radius,Circle_Radius)/Ring_Radius;
// create a tube as a polyhedron
// tube can be open or closed

// polyhedron constructor

function poly(name,vertices,faces,debug=[],partial=false) =
    [name,vertices,faces,debug,partial];

function p_name(obj) = obj[0];
function p_vertices(obj) = obj[1];
function p_faces(obj) = obj[2];

module show_solid(obj) {
    polyhedron(p_vertices(obj),p_faces(obj),convexity=10);
};

// utility functions
function m_translate(v) = [ [1, 0, 0, 0],
                            [0, 1, 0, 0],
                            [0, 0, 1, 0],
                            [v.x, v.y, v.z, 1  ] ];

function m_rotate(v) =  [ [1,  0,         0,        0],
                          [0,  cos(v.x),  sin(v.x), 0],
                          [0, -sin(v.x),  cos(v.x), 0],
                          [0,  0,         0,        1] ]
                      * [ [ cos(v.y), 0,  -sin(v.y), 0],
                          [0,         1,  0,        0],
                          [ sin(v.y), 0,  cos(v.y), 0],
                          [0,         0,  0,        1] ]
                      * [ [ cos(v.z),  sin(v.z), 0, 0],
                          [-sin(v.z),  cos(v.z), 0, 0],
                          [ 0,         0,        1, 0],
                          [ 0,         0,        0, 1] ];

function vec3(v) = [v.x, v.y, v.z];
function transform(v, m)  = vec3([v.x, v.y, v.z, 1] * m);

function orient_to(centre,normal, p) = m_rotate([0, atan2(sqrt(pow(normal.x, 2) + pow(normal.y, 2)), normal.z), 0])
                     * m_rotate([0, 0, atan2(normal[1], normal[0])])
                     * m_translate(centre);

// solid from path

function circle_points(r, sides,phase=45) =
    let (delta = 360/sides)
    [for (i=[0:sides-1]) [r * sin(i*delta + phase), r *  cos(i*delta+phase), 0]];

function loop_points(step,min=0,max=360) =
    [for (t=[min:step:max-step]) f(t)];

function transform_points(list, matrix, i = 0) =
    i < len(list)
       ? concat([ transform(list[i], matrix) ], transform_points(list, matrix, i + 1))
       : [];

function tube_points(loop, circle_points,  i = 0) =
    (i < len(loop) - 1)
     ?  concat(transform_points(circle_points, orient_to(loop[i], loop[i + 1] - loop[i] )),
               tube_points(loop, circle_points, i + 1))
     : transform_points(circle_points, orient_to(loop[i], loop[0] - loop[i] )) ;

function loop_faces(segs, sides, open=false) =
   open
     ?  concat(
         [[for (j=[sides - 1:-1:0]) j ]],
         [for (i=[0:segs-3])
          for (j=[0:sides -1])
             [ i * sides + j,
               i * sides + (j + 1) % sides,
              (i + 1) * sides + (j + 1) % sides,
              (i + 1) * sides + j
             ]
        ] ,
        [[for (j=[0:1:sides - 1]) (segs-2)*sides  + j]]
        )
     : [for (i=[0:segs])
        for (j=[0:sides -1])
         [ i * sides + j,
          i * sides + (j + 1) % sides,
          ((i + 1) % segs) * sides + (j + 1) % sides,
          ((i + 1) % segs) * sides + j
         ]
       ]
     ;

//  path with hulls

module hulled_path(path,r) {
    for (i = [0 : 1 : len(path) - 1 ]) {
        hull() {
            translate(path[i]) sphere(r);
            translate(path[(i + 1) % len(path)]) sphere(r);
        }
    }
};

// smoothed path by interpolate between points

weight = [-1, 9, 9, -1] / 16;

function interpolate(path,n,i) =
        path[(i + n - 1) %n] * weight[0] +
        path[i]             * weight[1] +
        path[(i + 1) %n]    * weight[2] +
        path[(i + 2) %n]    * weight[3] ;

function subdivide(path,i=0) =
    i < len(path)
     ? concat([path[i]],
              [interpolate(path,len(path),i)],
              subdivide(path, i+1))
     : [];

function smooth(path,n) =
    n == 0
     ?  path
     :  smooth(subdivide(path),n-1);

function path_segment(path,start,end) =
    let (l = len(path))
    let (s = max(floor(start * 360 / l),0),
         e = min(ceil(end * 360 / l),l - 1))
    [for (i=[s:e]) path[i]];

function scale(path,scale,i=0) =
    i < len(path)
      ?  concat(
           [[path[i][0]*scale[0],path[i][1]*scale[1],path[i][2]*scale[2]]],
           scale(path,scale,i+1)
         )
      :  [];

function curve_length(step,t=0) =
    t < 360
      ?  norm(f(t+step) - f(t)) + curve_length(step,t+step)
      :  0;

function map(t, min, max) =
      min + t* (max-min)/360;

//  create a knot from a path

function path_knot(path,r,sides,kscale,phase=45,open=false)  =
  let(loop_points = scale(path,kscale))
  let(circle_points = circle_points(r,sides,phase))
  let(tube_points = tube_points(loop_points,circle_points))
  let(loop_faces = loop_faces(len(loop_points),sides,open))
  poly(name="Knot",
         vertices = tube_points,
         faces = loop_faces);

 // render_type function-1

R=2*Rope_Radius;

function Epicycloid(t,a,b,c,m) =
    [ (a+b)*cos(t)-b*cos(((a/b)+1)*t),
      (a+b)*sin(t)-b*sin(((a/b)+1)*t),
      sin(m*t)
    ];

function Epitrochoid(t,a,b,c,m) =
    [ (a+b)*cos(t)-c*cos(((a/b)+1)*t),
      (a+b)*sin(t)-c*sin(((a/b)+1)*t),
      sin(m*t)
    ];

function Hypocycloid(t,a,b,c,m) =
    [ (a-b)*cos(t)+b*cos(((a/b)-1)*t),
      (a-b)*sin(t)-b*sin(((a/b)-1)*t),
      sin(m*t)
    ];

function Hypotrochoid(t,a,b,c,m) =
    [ (a-b)*cos(t)+c*cos(((a/b)-1)*t),
      (a-b)*sin(t)-c*sin(((a/b)-1)*t),
      sin(m*t)
    ];

function Rhodonea_curve(t,a,b,c,m) =
    [ (a+b)*cos(a/b*t)*sin(t),
      (a+b)*cos(a/b*t)*cos(t),
      sin(m*t)
    ];

function f(t) =
(shape == 0) ? Hypotrochoid(t,Ring_Radius,Circle_Radius,Circle_Distance,Angle_Multiple/Angle_Divisor) :
(shape == 1) ? Epitrochoid(t,Ring_Radius,Circle_Radius,Circle_Distance,Angle_Multiple/Angle_Divisor) :
(shape == 2) ? Rhodonea_curve(t,Ring_Radius,Circle_Radius,Circle_Distance,Angle_Multiple/Angle_Divisor) :
(shape == 3) ? Epicycloid(t,Ring_Radius,Circle_Radius,Circle_Distance,Angle_Multiple/Angle_Divisor) :
(shape == 4) ? Hypocycloid(t,Ring_Radius,Circle_Radius,Circle_Distance,Angle_Multiple/Angle_Divisor) :
0;

Kscale=[vx,vy,vz];

Paths = [loop_points(Step,Start,End)];
for (k =[0:len(Paths) - 1]) {
   path = Paths[k];
   knot= path_knot(path,R,Sides,Kscale,Phase,Open);
   solid1=knot;  // apply transformations here
   show_solid(solid1);
};

```


[User]
I'm looking to design a robust, parametric toothpaste squeezer for 3D printing. I've seen a few designs out there, but I want something I can customize and that is sturdy enough for someone with thick fingers to use reliably. I'd like the design to be based on the general idea of a tube being rolled up, using a slotted body, a toothed axis and a lock to prevent unrolling. I want to be able to adjust the length and inner diameter of the squeezer body, the wall thickness, the width of the slot, and the radius of the axis.

Specifically, here are the parts I envision and their desired parametric control:

*   **Body:**
    *   The body should be a cylindrical or near-cylindrical form, with a lengthwise slot cut into the side for squeezing the tube.
    *   `laenge`: Inner length of the body along the cylinder axis.
    *   `radius`: Inner radius of the body.
    *   `ws`: Wall thickness of the body.
    *   `bs`: Width of the slot opening where the tube is inserted.
    *   `rc`: Corner radius of the body's ends - the corners on the end of the cylinder where the body opens up.
    *   `of`: Type of opening. Should be either `0` for rectangular or `1` for rounded.
*   **Axis (with Handle):**
    *   The axis should have a hexagonal cross-section to grip the tube, with a handle for turning. The axis should have a length to match the body.
    *   `ra`: Radius of the axis's hexagonal portion. The axis should also include a short hexagonal section to act as a ratchet mechanism. The handle should be comfortable to grip with thick fingers, potentially with rounded features. The handle should include an opening for the locking mechanism.
*   **Lock:**
    *   The lock will prevent the axis from unrolling. It should have a hexagonal hole to match the ratchet portion of the axis, and a simple body that can be screwed into the top of the body.
        *   Lock: a part that fits onto the axis and prevents it from turning backward.

I'd like to be able to render the parts separately for printing, or assembled for visualization and testing. Specifically, I'd like to set a `part` variable that allows me to render the whole assembled model or parts in the following way:
*   `0` render assembled model
*   `1` render all parts for printing side-by-side
*   `2` render only the body
*   `3` render only the axis with handle
*   `4` render only the lock piece

The code should include comments to help me understand the design. I'd like to keep the code simple, without using any external libraries like BOSL or MCAD. I'm trying to keep things pretty basic here. I would like the $fn variable to default to 100. I would also like for the handle to have at least one supporting arm and I would like for the lock piece to have a more smooth shape. I'd like the handle to have an opening that allows the user to thread the tube into the axis.

[Assistant]
Tubenquetsche.scad:
```
// -----------------------------------------------------
// Tubenquetsche (primaer fuer Zahnpasta-Plastik-Tuben)
//
//  Detlev Ahlgrimm, 24.11.2018
// -----------------------------------------------------

// what to render
part=0;       // [0:assembled, 1:all for printing, 2:body, 3:axis, 4:lock]

// inner length of body
laenge=72;    // [30:1:150]

// inner radius of body
radius=11;    // [5:30]

// wall thickness
ws=1.5;       // [1:0.05:4]

// width of opening
bs=2;         // [1:0.05:4]

// radius axis
ra=5;         // [2:10]

// opening
of=1;         // [0:rectangular / full, 1:rounded]

// rounding
rc=4;         // [0:5]

/* [Hidden] */
$fn=100;
zh=ra*0.866025; // 0.866025=sqrt(3)/2

// -----------------------------------------------------
//  d : Aussendurchmesser
//  h : Hoehe
// -----------------------------------------------------
module Ring(d, h) {
  rotate_extrude() translate([d/2-h/2, 0, 0]) circle(d=h);
}

// -----------------------------------------------------
//  der Wickel-Koerper
// -----------------------------------------------------
module Body() {
  difference() {
    hull() {                                    // der massive Koerper
      //cylinder(r=radius+ws, h=laenge+4*ws);
      //translate([-radius/2, -radius-ws, 0]) cube([radius, 1, laenge+4*ws]);
      translate([0, 0, rc]) Ring((radius+ws)*2, rc*2);      // abgerundet ist es huebscher
      translate([0, 0, laenge+4*ws-rc]) Ring((radius+ws)*2, rc*2);
      translate([-radius/2, -radius-ws+rc, rc]) rotate([0, 90, 0]) cylinder(r=rc, h=radius);
      translate([-radius/2, -radius-ws+rc, laenge+4*ws-rc]) rotate([0, 90, 0]) cylinder(r=rc, h=radius);
    }
    //translate([0, 0, 2*ws]) cylinder(r=radius, h=laenge);               // rund aushoehlen
    hull() {
      translate([0, 0, 2*ws       +rc]) Ring(radius*2, rc*2);      // abgerundet ist es huebscher
      translate([0, 0, 2*ws+laenge-rc]) Ring(radius*2, rc*2);
    }
    if(of==0) {
      translate([-radius/2, 0, 2*ws]) cube([2*radius, 2*radius, laenge]); // seitlich oeffnen
    } else {
      hull() {                                            // auch hier ist rund eigentlich huebscher
        translate([0, 0, 2*ws+radius+2*ws])        rotate([-90, 90, 0]) cylinder(r=radius-ws, h=2*radius);
        translate([0, 0, 2*ws+laenge-radius-2*ws]) rotate([-90, 90, 0]) cylinder(r=radius-ws, h=2*radius);
      }
    }
    translate([-bs/2, -radius-ws-0.1, 2*ws+rc]) cube([bs, 3*ws, laenge-2*rc]);  // Tuben-Quetsch-Schlitz
    translate([0, 0, -0.1]) rotate([0, 0, 0]) cylinder(r=ra+0.3, h=laenge+4*ws+0.2, $fn=6);  // Achsoeffnung
    // Und ja ! .... hier ist bewusst KEINE Drehung um 30 Grad [mehr] (damit die Spitze des Sechsecks oben waere).
    // Das wuerde nur die Chance erhoehen, dass sich das Filament am Ueberhang hochbiegt. Und dann
    // crashed irgendwann die Noozle aus der falschen Richtung dagegen und reisst das Teil vom Bett.
  }
}

// -----------------------------------------------------
//  die blosse Achse
// -----------------------------------------------------
module Axis() {
  cylinder(r=ra, h=2*ws, $fn=6);                                  // vordere Rastung
  translate([0, 0, 2*ws]) cylinder(r=zh, h=3*ws);                 // vorderer frei-dreh-Bereich
  translate([0, 0, 5*ws]) cylinder(r=ra, h=laenge-ws, $fn=6);     // Wickel-Bereich
  translate([0, 0, 4*ws+laenge]) cylinder(r=zh, h=3*ws);          // hinterer frei-dreh-Bereich
  translate([0, 0, 7*ws+laenge]) cylinder(r=ra, h=2*ws, $fn=6);   // Konterstueck-Halterung
}

// -----------------------------------------------------
//  die Achse mit Griff
// -----------------------------------------------------
module Handle() {
  difference() {
    union() {
      rotate([-90, 0, 0]) Axis();
      hull() {                                      // Griff: linker Arm
        translate([0, -1, 0]) rotate([-90, 0, 0]) cylinder(r=ra, h=1, $fn=6);
        translate([-15, -15, 0]) sphere(r=zh);
        //translate([ 15, -15, 0]) sphere(r=zh);
      }
      hull() {                                      // Griff: rechter Arm
        translate([0, -1, 0]) rotate([-90, 0, 0]) cylinder(r=ra, h=1, $fn=6);
        //translate([-15, -15, 0]) sphere(r=zh);
        translate([ 15, -15, 0]) sphere(r=zh);
      }
      hull() {                                      // Griff: links/rechts-Verbinder
        //translate([0, -1, 0]) rotate([-90, 0, 0]) cylinder(r=ra, h=1, $fn=6);
        translate([-15, -15, 0]) sphere(r=zh);
        translate([ 15, -15, 0]) sphere(r=zh);
      }
    }
    translate([-bs/2, 6*ws, -zh-0.1]) cube([bs, laenge-5*ws, 2*radius]);  // Wickel-Oeffnung
  }
}

// -----------------------------------------------------
//  Konterstueck
// -----------------------------------------------------
module Lock() {
  difference() {
    //cylinder(r=ra+ws, h=3*ws);
    hull() {
      translate([0, 0,      1]) Ring((ra+ws)*2, 2);
      translate([0, 0, 3*ws-1]) Ring((ra+2*ws)*2, 2);
    }
    translate([0, 0, ws]) cylinder(r1=ra+0.2, r2=ra+0.4, h=2*ws+0.1, $fn=6);
  }
}

if(part==0) {           // Montage-Ansicht
  rotate([0, 0, -90]) translate([0, 0, radius+ws-zh]) {
    translate([0, laenge+4*ws, zh]) rotate([90, 0, 0]) Body();
    translate([0, 0, zh]) rotate([0, 0, 0]) Handle();
    translate([0, laenge+10*ws, zh]) rotate([90, 0, 0]) Lock();
  }
  translate([0, -radius, 0]) linear_extrude(1) text(str("Length of slit@axis: ", laenge-5*ws, "mm"), size=5, valign="top");
} else if(part==1) {    // Druck - alles zusammen
  rotate([0, 0, -90]) {
    translate([0, laenge+4*ws, radius+ws]) rotate([90, 0, 0]) Body();
    translate([2*radius, 0, zh]) Handle();
    translate([4*radius, ra+ws, 0]) Lock();
  }
} else if(part==2) {    // Body
  rotate([0, 0, -90]) translate([0, laenge+4*ws, radius+ws]) rotate([90, 0, 0]) Body();
} else if(part==3) {    // Axis
  rotate([0, 0, -90]) translate([0, 0, zh]) Handle();
} else if(part==4) {    // Lock
  Lock();
}

```


[User]
Hey, I'm looking to create a tool for building fused clapton coils for vaping, and I need some help designing it in OpenSCAD. I have a basic idea and some measurements, but I'm struggling with making it fully parametric.

The tool I need is basically a block with a channel cut through it. This channel will be used to guide and align several strands of wire so that they can be wrapped together.  Here are the specific parameters I need:

*   **`width`**: The width of the block, this also serves as the diameter of the main cylinder, and the overall height of the block.
*   **`length`**:  The length of the tool, this is how long the wire guide is.
*   **`wireAWG`**: The gauge of the wire we'll be using. I need this to be an AWG value instead of a diameter in millimeters. I have a conversion function for that, if it helps.
*   **`wiresToFit`**:  The number of wires that will fit in the channel.
*   **`gapTolerance`**: A multiplier that will make the channel slightly larger than the wires, to allow for easier insertion/removal and imperfections in the wire diameter. This is a multiplier, e.g. 1.1 will result in a channel that is 10% larger than the wires it will contain.

The overall shape should be a cube with the edges rounded off - I achieved this in my attempt using an intersection of a cube, a cylinder, and a sphere.  The channel itself should be a rectangular cut that goes all the way through the tool. The width of the channel should be the `wireDiameter` calculated using the `awg_to_mm` function below (you can add that to your code), scaled by `gapTolerance`. The height of the channel should be `wireDiameter` multiplied by the `wiresToFit`, scaled by `gapTolerance`.

```openscad
function awg_to_mm(awg) = 0.127 * pow(92.0,(36.0 - awg) / 39.0);
```

I’d like to be able to adjust all the parameters at the top of the file. I'd really appreciate it if you could make this a single, parametric OpenSCAD file for me.

[Assistant]
ParametricFusedClaptonHelper_v2.scad:
```
/*********************************************************************/
/* Parametric Fused Clapton helper tool for wire building.           */
/* Desgined by: mastabug                                             */
/* Version: 1                                                        */
/* Released: 2018-02-08                                              */
/*********************************************************************/

/*********************************************************************/
/* Parameters that you can adjust to accomodate your specific needs. */
/*********************************************************************/
width = 8;
length = 12;
wireAWG = 28;
wiresToFit=4;
gapTolerance=1.12; /*Multiplier, how much bigger to make the hole compared to wire. */

/*********************************************************************/
/* Don't change below this unless you really know what you're doing! */
/*********************************************************************/
$fn=6;

function awg_to_mm(awg) = 0.127 * pow(92.0,(36.0 - awg) / 39.0);

difference() {
	intersection() {
		cube([width, width, length], center = true);
		cylinder(r= width/1.9, h = length, center = true);
		sphere(r = length/2);
	}
    wireDiameter = awg_to_mm(wireAWG);
    echo("WIRE DIAMETER", wireDiameter);
	rotate(90,0,0) cube([wireDiameter*gapTolerance, wireDiameter* wiresToFit*gapTolerance, length], center = true);
}
```


[User]
I need a parametric OpenSCAD design for a hub adapter for bicycle wheels, specifically for use with a truing stand. My front wheel hub is 74mm wide, which is too narrow for my truing stand, and I want to create adapters that will effectively increase the hub width to 100mm. I'll need two identical adapters, one for each side of the hub. The adapter needs to fit over the existing axle stubs on the hub. The axle hole should be wide enough for standard quick-release axles, probably around 5.4mm. I need to account for some tolerance for the adapter to slip onto the hub's axle stubs so that the adapter fits well, maybe 0.6mm additional diameter on the axle stubs.

The design should be parameterized so that I can easily adjust:
*   The diameter of the hub axle (the part the adapter slips onto), called `AXLE_DIA` which is currently 9mm
*   The tolerance on the diameter of the hub axle, called `AXLE_DIA_TOLERANCE`, currently 0.6mm.
*   The length of the axle stubs on the hub, called `AXLE_LENGTH`, which is currently 4mm
*   The diameter of the axle hole, called `AXLE_HOLE_DIA`, currently 5.4mm.
*   The base diameter of the hub itself, called `HUB_BASE_DIA`, which is currently 19mm
*   The actual width of the hub, called `HUB_LENGTH`, which is currently 74mm.
*   The target width, when the adapters are installed, called `HUB_TARGET_LENGTH`, currently 100mm.

I need the design to have a cylindrical base that fits flush to the hub with a cylindrical extension with an inner hole for the wheel's axle. It should include some kind of tolerance to allow it to slip over the axle stub, so the inner diameter where the hub fits will need to be slightly bigger than the axle diameter. Can you also make sure it's easily printable by not including sharp angles in the design? It would be helpful if the adapter part was designed to be centered so that it can be printed in either orientation to ensure that it's properly aligned when used. I'm open to any design suggestions to make it print nicely. I'd like to use the BOSL2 library if possible. I'll need you to generate all the .scad files.

[Assistant]
axle_adapter.scad:
```

$fs=0.01;
$fa=1;

AXLE_DIA = 9.0;
AXLE_DIA_TOLERANCE = 0.6; //give some additional room to ensure the adapter fits on the hub
AXLE_LENGTH = 4.0;
AXLE_HOLE_DIA = 5.4;

HUB_BASE_DIA = 19.0;
HUB_LENGTH = 74.0;
HUB_TARGET_LENGTH = 100.0;

OVERLAP = 1; // just to combine solid elements

ADAPTER_BASE_LENGTH = (HUB_TARGET_LENGTH - HUB_LENGTH) / 2;

module axle_hole()
{

  cylinder(h = HUB_TARGET_LENGTH + 2*AXLE_LENGTH + 2*OVERLAP, d=AXLE_HOLE_DIA, center=true);
}

module hub()
{
  difference()
  {
    union()
    {
      // fake hub body, just to show length
      cylinder(h=HUB_LENGTH, d=HUB_BASE_DIA, center=true);

      // axle stubs
      translate([0,0, HUB_LENGTH/2 - OVERLAP/2 + AXLE_LENGTH/2])
        cylinder(h=AXLE_LENGTH+OVERLAP, d=AXLE_DIA, center=true);
      translate([0,0, -HUB_LENGTH/2 + OVERLAP/2 - AXLE_LENGTH/2])
        cylinder(h=AXLE_LENGTH+OVERLAP, d=AXLE_DIA, center=true);
    }
    axle_hole();
  }
}

module adapter()
{
  difference() {
  translate([0,0,ADAPTER_BASE_LENGTH/2])
  {
    union(){
      cylinder(h=ADAPTER_BASE_LENGTH, d=HUB_BASE_DIA, center=true);
      translate([0,0,ADAPTER_BASE_LENGTH/2 - OVERLAP/2 + AXLE_LENGTH/2])
        cylinder(h=AXLE_LENGTH+OVERLAP, d=AXLE_DIA, center=true);
    }
  }

  // build the empty space that shall contain the axle stub later
  // (use hull to create smooth overhangs for easier printing...)
  helper_length = AXLE_LENGTH;
  translate([0,0,(AXLE_LENGTH+OVERLAP)/2-OVERLAP])
    hull()
    {
      cylinder(h=AXLE_LENGTH+OVERLAP, d=AXLE_DIA + AXLE_DIA_TOLERANCE, center=true);
      translate([0,0,(AXLE_LENGTH+OVERLAP)/2])
        cylinder(h=2*helper_length, d=AXLE_HOLE_DIA, center=true);
    }

  axle_hole();

  }

}

//
%hub();
color("blue", 0.7)
translate([0,0,HUB_LENGTH/2])
  adapter();
```


[User]
Hi, I'm looking for some help designing a customizable spice rack for 3D printing. I really like the look of this spice shelf/rack I found on Thingiverse by ceerzet (thing:3128614), but it's designed to hang from a shelf and I need something that will mount to a wall. I also would like to use dowels instead of metal tubes for the mounting rods.

I've tried remixing it myself, but I'm not very good at OpenSCAD. I've incorporated the original .stl file, and added some code to extend the depth and put in the holes for the dowels, plus I included the rounded primitives library by Wouter Robers (thing:58478) in my file. I can share that if it helps. I'd like to do this the right way though, with parameters.

Basically, I want a wall-mounted spice rack that is parametric. I need to be able to adjust:

*   The radius of the through-rod hole (for hanging or stacked mounting). I want a minimum of zero (no hole) to around 2.7mm to accommodate different hardware. This could be a single hole going through the rack or a series of holes.
*   The length of the mounting posts, which should be configurable and allow for both increases and decreases to the default of 140mm.
*   The diameter of the dowel/tubing I’ll be using, probably between 6 and 11mm.
*    A printer factor for tolerances – the default value being 0.2 for a 0.4mm nozzle

I'm not sure how to best incorporate the original spice_shelf.stl, maybe as a hull or something? I want the "look" of the original spice shelf but don't want to be bound to the exact dimensions. Ideally, the shelf section should be treated as a separate "object" that can be adjusted (e.g. by scaling and adjusting the depth). I'd like it to be adjustable parametrically in terms of its overall depth/width/height. The dowels should attach directly to the sides of this shelf, and the mounting posts would extend from the back of the shelf.

I'm using OpenSCAD, and would like to get a .scad file that I can customize.

[Assistant]
CustomSpiceShelfRack.scad:
```
/* Open SCAD Name.: CustomSpiceShelfRack.scad
*              by.: lapope47
*  Creation Date..: 11/20/2018
*  Description....: This is a remix of the kitchen spice shelf / rack by
*  ceerzet (https://www.thingiverse.com/thing:3128614).  This program
*  contains ceezert's original "spice_shelf.STL" file.
*
*
*
*  NOTE; Output.stls may need to be repaired before sending to slicer.
*/

/*------------------Customizer View-------------------*/

// preview[view:north, tilt:top]

/*--------------------- Includes ---------------------*/
//include <rcube.scad>; // incorporated in program

/*---------------------Parameters---------------------*/

// desired thru-rod radius for hanging or stacked mounting(0 to ~2.7mm)
rod = 2.2; // (2.2 typical for M4 or #8 )

// desired change to mounting post length (default length = 140mm)
len = 0; // (-20 to + ~50, depending on printer)

// dowel or tubing diameter (~6 -- 11mm)
dd = 9.2; // (9.2 typical for 3/8" dowel)

// printer factor (typical = 0.2 for 0.4 nozzle)
pf = 0.2;

// hole radius for dowel
hr = dd/2 + pf;

/*-----------------------Execute----------------------*/

translate([115,-1,9.7])rotate([-90,0,90])
main_module();

/*-----------------------Modules----------------------*/

module main_module(){ //create module
$fn=60;
    difference() {
       union() {//start union
          //translate([1,0,0])scale([1.2,1.2,1.2])import("OrigSTL/spice_shelf.STL", convexity = 10);
            translate([1,0,0])scale([1.2,1.2,1.2])import("spice_shelf.STL", convexity = 10);
            translate([1,0,-25-len])cube([8,9.7,140+len]);
            translate([16.8,0,107])rotate([-90,0,0])rcylinder(r1=4,r2=4,h=9.7,b=3);
            translate([36,0,107])rotate([-90,0,0])rcylinder(r1=4,r2=4,h=9.7,b=3);
            translate([55,0,107])rotate([-90,0,0])rcylinder(r1=4,r2=4,h=9.7,b=3);
            translate([70.8,0,88])rotate([-90,0,0])rcylinder(r1=4,r2=4,h=9.7,b=3);
            translate([70.8,0,69])rotate([-90,0,0])rcylinder(r1=4,r2=4,h=9.7,b=3);
            }// end union

    //start subtraction of difference
     # translate([5,4.85,-35-len])cylinder(160+len,rod,rod);
      translate([16.8,-1,107])rotate([-90,0,0])cylinder(19,hr,hr);
      translate([36,-1,107])rotate([-90,0,0])cylinder(19,hr,hr);
      translate([55,-1,107])rotate([-90,0,0])cylinder(19,hr,hr);
      translate([70.8,-1,88])rotate([-90,0,0])cylinder(19,hr,hr);
      translate([70.8,-1,69])rotate([-90,0,0])cylinder(19,hr,hr);
      } //end difference
}//end module

/* Library included below to allow customizer functionality
 *      Rounded primitives for openscad
 *      (c) 2013 Wouter Robers
 *      Syntax example for a rounded block
 *      translate([-15,0,10]) rcube([20,20,20],2);
 *
 *      Syntax example for a rounded cylinder
 *      translate([15,0,10]) rcylinder(r1=15,r2=10,h=20,b=2);
 *
 * http://www.thingiverse.com/thing:58478
 *
 * CC Public Domain
 */

module rcube(Size=[20,20,20],b=2)
{hull(){for(x=[-(Size[0]/2-b),(Size[0]/2-b)]){for(y=[-(Size[1]/2-b),(Size[1]/2-b)]){for(z=[-(Size[2]/2-b),(Size[2]/2-b)]){ translate([x,y,z]) sphere(b);}}}}}

module rcylinder(r1=10,r2=10,h=10,b=2)
{translate([0,0,-h/2]) hull(){rotate_extrude() translate([r1-b,b,0]) circle(r = b); rotate_extrude() translate([r2-b, h-b, 0]) circle(r = b);}}

```


[User]
I'm looking to create a customizable truss bracket generator in OpenSCAD. I've seen some designs that come close, but I want something a bit more flexible.

Here's the basic concept: I need a bracket that can connect up to 8 beams at various angles. The beams will be square or rectangular and will intersect at a common point in the center of the bracket. The bracket itself will have a flat, flange-like base that extends out from the beams and a raised surround that will grip the sides of the beams.

The key features I'd like to parameterize are:

*   **Beam Angles:** Each of the up to 8 beams should have a settable angle, or the option to be disabled (not included in the bracket).
*   **Beam Widths:** Each beam should have a settable width (or be disabled), which determines the pocket size of the surround.
*   **Beam Lengths:** Each beam should have a settable length of the flange, which determines the length of the flange extending out along each beam.
*   **Flange Thickness:** The thickness of the flat flange base.
*   **Flange Width:** The width of the flat flange base.
*   **Fillet Radius:** The radius of the fillets at all corners where the surround meets the flange.
*   **Surround Height:** The height of the surround that grips the beam sides.
*  **Surround Width:** The width of the surround gripping the beam.
*   **Offset Distance:** An optional offset distance to shift the surround either inside or outside the beam, which might be useful for slightly varying fits.
*   **Tolerance:** A global tolerance value, mainly to account for potential overextrusion and variations in beam dimensions.
*   **Mask Edge:**  A boolean that, if enabled, uses an edge mask, so the flange extends only to the beams instead of being a solid disk.

I'd prefer it if you could generate the following for me:

*   A main scad file that is intended for metric units.
*   A second main scad file that uses imperial units (inches).
*   A single scad file that contains the parametric module definition for creating the bracket. This file should be imported into both main scad files. This module needs to be able to take all of the parameters as inputs so it is flexible.

Essentially, I'm trying to be able to define all these dimensions and angles and get a ready-to-print bracket. I'm hoping that you can make these a single parametric module in a separate file, then call it from either a metric main file or an imperial main file.

[Assistant]
truss_bracket_metric.scad:
```
/*
 * Configurable Strut Bracket
 *
 * by 4iqvmk, 2018-12-08
 * https://www.thingiverse.com/4iqvmk/about
 *
 * Licenced under Creative Commons Attribution - Share Alike 4.0
 *
 * version 1.00 - initial version
*/

/* [Hidden] */

inch2mm = 0.3048/12*1000;
mm2inch = 12/0.3048/1000;
$fn=30;

/* [Parameters] */

// mm
flangeThickness = 1.25;

// mm
flangeWidth = 6;

// mm
filletRadius = 2;

// mm
surroundHeight = 3.0;

// mm
surroundWidth = 1.5;

// mm
offsetDistance = 0;

// mm
tolerance = 0.05; // [0:0.01:0.2]

maskEdge = 1;  // [0: false, 1: true]

/* [Beam Angles] */

// deg, or -1 to disable
beam1Angle = 0;

// deg, or -1 to disable
beam2Angle = 30;

// deg, or -1 to disable
beam3Angle = 90;

// deg, or -1 to disable
beam4Angle = 150;

// deg, or -1 to disable
beam5Angle = 180;

// deg, or -1 to disable
beam6Angle = -1;

// deg, or -1 to disable
beam7Angle = -1;

// deg, or -1 to disable
beam8Angle = -1;

/* [Beam Widths] */

// mm, or -1 to disable
beam1Width = 4.5;

// mm, or -1 to disable
beam2Width = 3;

// mm, or -1 to disable
beam3Width = 3;

// mm, or -1 to disable
beam4Width = 3;

// mm, or -1 to disable
beam5Width = 4.5;

// mm, or -1 to disable
beam6Width = 3;

// mm, or -1 to disable
beam7Width = 3;

// mm, or -1 to disable
beam8Width = 3;

/* [Flange Lengths] */

// mm, or -1 to disable
beam1Length = 30;

// mm, or -1 to disable
beam2Length = 25;

// mm, or -1 to disable
beam3Length = 20;

// mm, or -1 to disable
beam4Length = 25;

// mm, or -1 to disable
beam5Length = 30;

// mm, or -1 to disable
beam6Length = 30;

// mm, or -1 to disable
beam7Length = 30;

// mm, or -1 to disable
beam8Length = 30;

/* [Hidden] */

/////////////////////////////////////////////////////////////////////////////
// Calculated values...
/////////////////////////////////////////////////////////////////////////////
allAngles = [beam1Angle, beam2Angle, beam3Angle, beam4Angle, beam5Angle, beam6Angle, beam7Angle, beam8Angle];
allWidths = [beam1Width, beam2Width, beam3Width, beam4Width, beam5Width, beam6Width, beam7Width, beam8Width];
allLengths = [beam1Length, beam2Length, beam3Length, beam4Length, beam5Length, beam6Length, beam7Length, beam8Length];

makeTruss(allAngles, allWidths, allLengths, flangeThickness, flangeWidth, filletRadius, surroundHeight, surroundWidth, offsetDistance, tolerance, maskEdge);

module makeTruss(allAngles, allWidths, allLengths, flangeThk, flangeWid, filletRad, surroundHgt, surroundWid, offsetDist, tol, maskEdge) {

    iBeam = [ for (k = [0:len(allAngles)-1]) (allAngles[k]>=0 && allWidths[k] > 0 && allLengths[k] > 0) ? true : false ];

    beamAngles = [ for (k = [0:len(allAngles)-1]) if (iBeam[k]) allAngles[k]];
    beamWidths = [ for (k = [0:len(allAngles)-1]) if (iBeam[k]) allWidths[k]];
    beamLengths = [ for (k = [0:len(allLengths)-1]) if (iBeam[k]) allLengths[k]];

    nBeams = len(beamAngles);

    beams = [ for (k=[0:nBeams-1]) [beamAngles[k], beamWidths[k], beamLengths[k]] ];

    fullBeams = [ for (k=[0:nBeams-1]) [beamAngles[k], beamWidths[k]+2*flangeWid+2*tol, beamLengths[k]] ];

    fullSurround = [ for (k=[0:nBeams-1]) [beamAngles[k], beamWidths[k] + 2*surroundWid + 2*tol, beamLengths[k]] ];

    iMin = index_min(beamAngles);
    iMax = index_max(beamAngles);

    beamA = beams[iMin];
    beamB = beams[iMax];

    maxLength = max(beamLengths);
    maxWidth = max(beamWidths);

    difference() {

        union() {

            if (maskEdge) {
                union() {
                    difference() {
                        makeFlange(fullBeams, flangeThk, filletRad);
                        outerMask(beamA, beamB, filletRad, offsetDist, maxLength, flangeThk, surroundHgt, maxWidth*1.5);
                    }
                    difference() {
                        makeFlange(fullSurround, flangeThk+surroundHgt, filletRad);
                        outerMask(beamA, beamB, filletRad, min(offsetDist,0), maxLength, flangeThk, surroundHgt, maxWidth*1.5);
                    }
                }
            } else {
                makeFlange(fullBeams, flangeThk, filletRad);
                makeFlange(fullSurround, flangeThk+surroundHgt, filletRad);
            }
        }

        makeBeamMask(beams, flangeThk, surroundHgt, filletRad, tol);
    }
}

module makeFlange(beam, height, filletRad) {
    linear_extrude(height = height) {
        soften_profile(radius=filletRad) {
            union() {
                for (k = [0:len(beam)-1]) {
                    flangeLeg(beam[k]);
                }
            }
        }
    }
}

module makeBeamMask(beam, flangeT, surroundH, filletR, tol) {
    translate([0, 0, flangeT]) {
        linear_extrude(height = 2*surroundH) {
            soften_profile(radius=filletR) {
                union() {
                    for (k = [0:len(beam)-1]) {
                        beamMask(beam[k], tol);
                    }
                }
            }
        }
    }
}

module flangeLeg(b) {

    angle = b[0];
    width = b[1];
    length = b[2];

    if (angle>=0) {
        rotate(a=angle, v=[0,0,1]) {
            union() {
                translate([length/2, 0, 0]) {
                    square([length, width], true);
                }
                circle(r=width/2);
            }
        }
    }
}

module beamMask(b, tol) {

    angle = b[0];
    width = b[1];
    length = b[2];

    if (angle>=0) {
        rotate(a=angle, v=[0,0,1]) {
            union() {
                translate([length, 0, 0]) {
                    square([length*2, width+tol], true);
                }
                circle(r=width/2);
            }
        }
    }
}

// remove the outside edge if selected
module outerMask(beamA, beamB, rad, off, maxLength, flangeT, surroundH, filletR) {

    r = 2*maxLength;
    n = 16;

    maxHeight = flangeT + surroundH;

    // angle half way from beamB around to beamA
    intAngle = wrap(0.5 * wrap360(beamA[0] - beamB[0]) + beamB[0]);

    // shift distance for mask

    linear_extrude(height = 4*maxHeight, center=true) {

        soften_profile(radius=filletR) {
            union() {

                dxyA = (beamA[1]/2 + off) / abs(sin(0.5*wrap(beamA[0] - beamB[0])));

                dxyB = (beamB[1]/2 + off) / abs(sin(0.5*wrap(beamA[0] - beamB[0])));

                translate([dxyA*cos(intAngle), dxyA*sin(intAngle), 0]) {

                    for (k=[0:n-1]) {

                        // intermediate angles from beamB around to beamA in n increments
                        intAngle0 = wrap(k/n * (wrap360(beamA[0] - intAngle)) + intAngle);
                        intAngle1 = wrap((k+1)/n * (wrap360(beamA[0] - intAngle)) + intAngle);

                        polygon(points = [ [0,0], [r*cos(intAngle0), r*sin(intAngle0)], [r*cos(intAngle1), r*sin(intAngle1)],  [0,0]]);
                    }
                }
                translate([dxyB*cos(intAngle), dxyB*sin(intAngle), 0]) {

                    for (k=[0:n-1]) {

                        // intermediate angles from beamB around to beamA in n increments
                        intAngle0 = wrap(k/n * (wrap360(intAngle - beamB[0])) + beamB[0]);
                        intAngle1 = wrap((k+1)/n * (wrap360(intAngle - beamB[0])) + beamB[0]);

                        polygon(points = [ [0,0], [r*cos(intAngle0), r*sin(intAngle0)], [r*cos(intAngle1), r*sin(intAngle1)],  [0,0]]);
                    }
                }
            }
        }
    }
}

function wrap(a) = atan2(sin(a), cos(a));

function wrap360(a) = wrap(a) - 360*floor(wrap(a)/360);

function index_min(x) = search(min(x), x)[0];

function index_max(x) = search(max(x), x)[0];

module soften_profile(radius) {
    offset(r=-0.5*radius) {
        offset(r=1.0*radius) {
            offset(r=-0.5*radius) {
                children();
            }
        }
    }
}

```
truss_bracket_inches.scad:
```
/*
 * Configurable Strut Bracket
 *
 * by 4iqvmk, 2018-12-08
 * https://www.thingiverse.com/4iqvmk/about
 *
 * Licenced under Creative Commons Attribution - Share Alike 4.0
 *
 * version 1.00 - initial version
*/

/* [Hidden] */

inch2mm = 0.3048/12*1000;
mm2inch = 12/0.3048/1000;
$fn=30;

use <truss_bracket_metric.scad>

/* [Parameters] */

// in
flangeThickness = 1/32;

// in
flangeWidth = 1/4;

// in
filletRadius = 1/16;

// in
surroundHeight = 1/8;

// in
surroundWidth = 1/32;

// in
offsetDist = 0;

// in
tol = 0.005; // [0:0.001:0.01]

maskEdge = 1;  // [0: false, 1: true]

/* [Beam Angles] */

// deg, or -1 to disable
beam1Angle = 0;

// deg, or -1 to disable
beam2Angle = 30;

// deg, or -1 to disable
beam3Angle = 90;

// deg, or -1 to disable
beam4Angle = 150;

// deg, or -1 to disable
beam5Angle = 180;

// deg, or -1 to disable
beam6Angle = -1;

// deg, or -1 to disable
beam7Angle = -1;

// deg, or -1 to disable
beam8Angle = -1;

/* [Beam Widths] */

// in, or -1 to disable
beam1Width = 1/8;

// in, or -1 to disable
beam2Width = 1/8;

// in, or -1 to disable
beam3Width = 1/8;

// in, or -1 to disable
beam4Width = 1/8;

// in, or -1 to disable
beam5Width = 1/8;

// in, or -1 to disable
beam6Width = 1/8;

// in, or -1 to disable
beam7Width = 1/8;

// in, or -1 to disable
beam8Width = 1/8;

/* [Flange Lengths] */

// in, or -1 to disable
beam1Length = 1.25;

// in, or -1 to disable
beam2Length = 1;

// in, or -1 to disable
beam3Length = 7/8;

// in, or -1 to disable
beam4Length = 1;

// in, or -1 to disable
beam5Length = 1.25;

// in, or -1 to disable
beam6Length = 1.25;
//
// in, or -1 to disable
beam7Length = 1.25;

// in, or -1 to disable
beam8Length = 1.25;

/* [Hidden] */

/////////////////////////////////////////////////////////////////////////////
// Calculated values...
/////////////////////////////////////////////////////////////////////////////
allAngles = [beam1Angle, beam2Angle, beam3Angle, beam4Angle, beam5Angle, beam6Angle, beam7Angle, beam8Angle];
allWidths = [beam1Width, beam2Width, beam3Width, beam4Width, beam5Width, beam6Width, beam7Width, beam8Width];
allLengths = [beam1Length, beam2Length, beam3Length, beam4Length, beam5Length, beam6Length, beam7Length, beam8Length];

makeTruss(allAngles, allWidths*inch2mm, allLengths*inch2mm, flangeThickness*inch2mm, flangeWidth*inch2mm, filletRadius*inch2mm, surroundHeight*inch2mm, surroundWidth*inch2mm, offsetDist*inch2mm, tol*inch2mm, maskEdge);

```


[User]
I'd like to design some parametric brackets for mounting network devices inside a Leviton Structured Media Center (SMC) enclosure. I'm envisioning a system where I can specify the dimensions of the devices I want to mount, and the bracket will automatically adjust. I need both left-hand and right-hand versions of the brackets.

Here's a breakdown of what I need:

**General Structure:**

*   The brackets will attach to the standard mounting holes in the SMC. These holes are arranged in a grid pattern.
*   I need to be able to specify the spacing between the holes both vertically (row spacing) and horizontally (column spacing). I expect the column spacing to possibly have multiple values in a repeating pattern.
*   The brackets will have a base that attaches to the SMC, and a support structure to hold the device.

**Key Parameters:**

*   **`row_spacing_inches`**: The vertical distance between holes in inches. (e.g. 1 inch)
*   **`col_spacing_inches`**: An array of horizontal distances between holes in inches, which will repeat. (e.g. `[3, 3, 0.625]`)
*   **`part`**: A string to switch between "left" and "right" bracket designs.
*   **`numberOfRows`**: The number of rows of mounting holes the bracket will span.
*   **`flangeXX`**: Boolean values to determine if a flange (either blank or with a hole) should be created on each row on the base of the mount. I'll need 12 of these variables, `flange01` through `flange12`. Values will be `0` (none), `1` (blank), or `2` (hole).
*   **`deviceWidthInInches`**: The width of the device that the bracket will hold. If this is set, the support block's horizontal offset will be calculated.
*   **`supportBlockXOffsetInches`**: The default horizontal offset for the device support block, in inches.
*   **`supportBlockWidthInches`**: The width of the device support block in inches.
*  **`sideStopWidthInches`**: The width of the side stop on the support block in inches.
*   **`endStopHeightInches`**: The height of the end stop on the support block in inches.
*   **`endStopWidthInches`**: The width of the end stop on the support block in inches.
*   **`strapSlotWidthInches`**: The width of the strap slot if the `support_blockXX` is set to `2`.
*  **`support_blockXX`**: Integer values to select the type of support block. I'll need 12 of these variables, `support_block01` through `support_block12`. Valid values are `0` (None), `1` (Block), `2` (BlockWithStrap), `3` (EndStop), `4` (SideStop), `5` (EndStopWithSideStop).

**Bracket Features:**

*   **Flanges:** Each row should allow an option to have a flange that is either blank or has a mounting hole.
*   **Support Blocks:** These blocks will hold the devices. They can be:
    *   A basic block.
    *   A block with a strap slot for a Velcro strap.
    *  A block with an end-stop.
    *  A block with a side stop
    * A block with both an end stop and a side stop
*   **Holes:** The mounting holes should be countersunk for flat-head screws.

**Output:**

*   Two .scad files: one for the "left" bracket and one for the "right" bracket. The "right" bracket should be a mirror image of the "left" bracket.

**Units:**

*   All dimensions should be in millimeters, but the input parameters should be in inches and internally converted.

I'm not looking to use any specific external libraries, but I'd like the design to be modular and easy to modify in the future.

[Assistant]
structured_media_center_bracket_generator.scad:
```
// Structured Media Center bracket generator
// Copyright 2018 - Mike Ryan (falter@gmail.com)

/* [Spacing] */
// Spacing between holes (center-to-center) of rows, in inches.
row_spacing_inches=1;
// Spacing between holes (center-to-center) of columns, in inches.
col_spacing_inches=[3,3,0.625];

/* [Main] */
part = "left"; // [left:Left Mount, right:Right Mount]
// number of rows
numberOfRows = 5; // [1:12]

/* [End Stop and Device Width Options] */

/* [Flange Options] */
// Type of Flange in Row 1
flange01 = 2; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 2
flange02 = 2; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 3
flange03 = 2; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 4
flange04 = 2; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 5
flange05 = 2; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 6
flange06 = 0; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 7
flange07 = 0; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 8
flange08 = 0; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 9
flange09 = 0; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 10
flange10 = 0; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 11
flange11 = 0; // [0:None, 1:Blank, 2:Hole]
// Type of Flange in Row 12
flange12 = 0; // [0:None, 1:Blank, 2:Hole]

/* [Support Block Options] */
// If set, a supportBlockXOffsetInches will be determined that will fit a device of deviceWidthInInches width
deviceWidthInInches = 0;

// support block x offset in inches
supportBlockXOffsetInches = 0.625;

// support block width in inches
supportBlockWidthInches = 0.625;

// support block x offset in inches
sideStopWidthInches= 0.0625;

// end stop height in inches
endStopHeightInches = 0.5;
// end stop width in inches
endStopWidthInches = 0.625;
// strap slot width in inches
strapSlotWidthInches = 0.75;
// Type of Support Block in Row 1
support_block01 = 5; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 2
support_block02 = 2; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 3
support_block03 = 2; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 4
support_block04 = 2; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 5
support_block05 = 2; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 6
support_block06 = 0; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 7
support_block07 = 0; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 8
support_block08 = 0; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 9
support_block09 = 0; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 10
support_block10 = 0; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 11
support_block11 = 0; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]
// Type of Support Block in Row 12
support_block12 = 0; // [ 0:None, 1:Block, 2:BlockWithStrap, 3:EndStop, 4:SideStop, 5:EndStopWithSideStop ]

/* [Hidden] */
inch = 25.4;
$fn = 36;
// Constants
FLANGE_NONE  = 0;
FLANGE_BLANK = 1;
FLANGE_HOLE  = 2;

SUPPORT_BLOCK_NONE = 0;
SUPPORT_BLOCK_BLOCK = 1;
SUPPORT_BLOCK_WITH_STRAP = 2;
SUPPORT_BLOCK_WITH_END_STOP = 3;
SUPPORT_BLOCK_WITH_SIDE_STOP = 4;
SUPPORT_BLOCK_WITH_END_STOP_AND_SIDE_STOP = 5;

vertical_hole_spacing = row_spacing_inches * inch;
hole_diameter = 1/4 * inch;
hole_thickness = 1/16 * inch;
countersunk_hole_diameter = 7/16 * inch;
flange_thickness = 1/8 * inch;
flange_width_max = 5/8 * inch;
stop_side_width = sideStopWidthInches * inch;

function calc_block_x_offset_for_width(width, column = 0, r = -1) = r >= 0 ? r : calc_block_x_offset_for_width(width, column+1, r=((col_right_flange_offset(column) - col_left_flange_offset(0) - width)/2 - stop_side_width));

support_block_x_offset = deviceWidthInInches > 0 ? calc_block_x_offset_for_width(deviceWidthInInches*inch) : supportBlockXOffsetInches*inch;

flange_rows = [flange01,flange02,flange03,flange04,flange05,flange06,flange07,flange08,flange09,flange10,flange11,flange12];
support_block_rows = [support_block01,support_block02,support_block03,support_block04,support_block05,support_block06,support_block07,support_block08,support_block09,support_block10,support_block11,support_block12];

print_part();

module print_part() {
    if (part == "left") {
        make_mount();
    } else if (part == "right") {
        mirror([1,0,0]) make_mount();

    }
}

module make_mount() {
    scmMount(num_rows=numberOfRows,
                    stop_height=endStopHeightInches*inch,
                    support_block_width=supportBlockWidthInches * inch,
                    flange_rows=flange_rows,
                    support_block_rows=support_block_rows,
                    support_block_x_offset=support_block_x_offset,
                    stop_bottom_width=endStopWidthInches * inch,
                    stop_side_width=stop_side_width,
                    strap_slot_width=strapSlotWidthInches * inch
    );
}

function col_hole_offset(x = 0, i = 0, r = 0) =  i < x ? col_hole_offset(x, i + 1, r + col_spacing_inches[i%len(col_spacing_inches)]) : r*inch+flange_width_max/2;
function col_left_flange_offset(x) =  col_hole_offset(x) - flange_width_max/2;
function col_right_flange_offset(x) =  col_hole_offset(x) + flange_width_max/2;
function col_capacity(x) = col_right_flange_offset(x) - col_left_flange_offset(0) - 2*(support_block_x_offset+stop_side_width);
function row_hole_offset(y) = vertical_hole_spacing/2 + y * vertical_hole_spacing;
function row_offset_min(y) = row_hole_offset(y) - vertical_hole_spacing/2;
function row_offset_max(y) = row_offset_min(y) + vertical_hole_spacing;

module cabinetHoles() {
    holes(5,25,5);
}

module scmMount(num_rows=3,
                    strap_slot_width=0.75*inch,
                    support_block_x_offset=flange_width_max,
                    support_block_width=0.5*inch,
                    support_block_height=0.5*inch,
                    flange_rows=[],
                    support_block_rows=[],
                    stop_side_width=1/16*inch,
                    stop_height=0.5*inch,
                    stop_bottom_length=0.25*inch,
                    stop_bottom_width=0.5*inch,
                    ) {

    // TODO: use assert?
    if (support_block_x_offset < 0) {
        echo("ERROR: support_block_x_offset cannot be < 0");
    }

    all_rows = [ for ( zz = [0:1:num_rows-1] ) zz ] ;
    flange_width = max(flange_width_max, support_block_x_offset);
    flange_length = row_hole_offset(num_rows-1) + vertical_hole_spacing/2;
    flange_dims = [flange_width, vertical_hole_spacing, flange_thickness];
    support_block_dims = [support_block_width, vertical_hole_spacing, support_block_height];
    stop_bottom_dims = [stop_bottom_width, stop_bottom_length, stop_height];

    difference() {
        union() {
            for (x=[0:1:num_rows-1]) {
                translate([0,row_offset_min(x)]) {
                    // Create the flanges.
                    make_flange(flange_dims, flange_rows[x]);

                    translate([support_block_x_offset, 0, 0]) {
                        // Create the support blocks
                        if (support_block_rows[x] == SUPPORT_BLOCK_NONE) {
                            // NOP
                        } else if (support_block_rows[x] == SUPPORT_BLOCK_BLOCK) {
                            basic_support_block(support_block_dims);
                        } else if (support_block_rows[x] == SUPPORT_BLOCK_WITH_STRAP) {
                            strap_support(support_block_dims, strap_slot_width);
                        } else if (support_block_rows[x] == SUPPORT_BLOCK_WITH_END_STOP_AND_SIDE_STOP) {
                            make_stop_block(support_block_dims, stop_bottom_dims, stop_side_width);
                        } else if (support_block_rows[x] == SUPPORT_BLOCK_WITH_END_STOP) {
                            make_stop_block(support_block_dims, stop_bottom_dims, 0);
                        } else if (support_block_rows[x] == SUPPORT_BLOCK_WITH_SIDE_STOP) {
                            make_stop_block(support_block_dims, [0,0,stop_bottom_dims[2]], stop_side_width);
                        }

                    }
                }
            }
        }
        // Trim both the ends
        translate([0,0,0]) cube([support_block_x_offset + support_block_width, 1/32*inch, support_block_height+stop_height]);
        translate([0,row_offset_max(num_rows-1),0]) cube([support_block_x_offset + support_block_width, 1/32*inch, support_block_height+stop_height]);
    }

}

module basic_support_block(support_block_dims) {
    cube(support_block_dims);
}

module make_flange(flange_dims, mode) {
    difference() {
        if (mode == FLANGE_BLANK) {
            cube(flange_dims);
        } else if (mode == FLANGE_HOLE) {
            difference() {
                cube(flange_dims);
                hole(0,0);
            }
        } else if (mode == FLANGE_NONE) {
            // Nop

        } else {
            echo("ERROR: unknown flange mode: ", mode);
        }
    }
}

module strap_support(support_block_dims, strap_slot_width) {
    difference() {
        basic_support_block(support_block_dims);
        translate([0, support_block_dims[1]/2, 1/4*inch]) {
            rotate(a=[0,-45,0]) cube([1*inch, strap_slot_width, (1/16)*inch], center=true);
        };
    }
}

module make_stop_block(support_block_dims, stop_bottom_dims, stop_side_width) {
    basic_support_block(support_block_dims);
    translate([0,0,support_block_dims[2]-1]) {
        // Make bottom stop
        if (stop_bottom_dims[0] > 0 && stop_bottom_dims[1] > 0 && stop_bottom_dims[2] > 0) {
            hull() {
                cube([stop_bottom_dims[0], 1, 1]);
                translate([0,stop_bottom_dims[1],0])
                    cube([stop_bottom_dims[0], 1, stop_bottom_dims[2]+1]);
            }
        }
        // Make side stop
        if (stop_side_width > 0) {
                hull() {
                    translate([0,stop_bottom_dims[1]])
                        cube([stop_side_width,1,stop_bottom_dims[2]+1]);
                    translate([0,row_offset_max(0)-1, 0])
                        cube([stop_side_width,1,stop_bottom_dims[2]+1]);
                }
        }
    }
}

module holeOffset(column, row) {
    translate([col_hole_offset(column), row_hole_offset(row)]) children();
}

module hole(column, row) {
    holeOffset(column,row) {
        cylinder(d=hole_diameter, h = hole_thickness);
        translate([0,0,hole_thickness])
            cylinder(d=countersunk_hole_diameter, h = 10);
    }
}

// Lays out a pattern of holes
module holes(columns, rows, height) {
    for (x=[0:1:columns]) {
        for (y=[0:1:rows]) {
            hole(x,y, height);
        }
    }
}
```


[User]
I'm looking to design a PTFE tube holder for my Prusa MMU2. I've got a design I'm working with, but I need to make it parametric so I can adjust it to fit various tube sizes and mounting setups, and I could also use some help creating the entire thing as I'm having trouble with my current method.

The basic idea is that I want to have a block that holds the PTFE tubes, which I can then slot into a holder. The tubes should be held vertically in a row, parallel to each other. The holder itself should have some side walls and an edge to keep the block in place, but it should still allow the block to be removed. This will all be used in conjunction with the "MMU2 filament control" from Thingiverse (link: https://www.thingiverse.com/thing:3184377).

Here are the important dimensions and features I need to control via parameters:

*   **`tube_diameter`**: The diameter of the holes for the PTFE tubes (default 4.1mm). I want to be able to change this to accommodate different sized tubes.
*   **`width`**: The total width of the holder. I'm aiming for around 65mm for the default, but I want to be able to adjust it so it can mount to other locations or fit different configurations.
*   **`height`**: The overall height of the holder and block (combined). This is crucial for making sure it doesn't interfere with removing the block, I'm aiming for 31.5mm as a starting point but need it to be parametric.
*   **`thick`**: The thickness of the block that will hold the PTFE tubes, I'd like to be able to control that between 2-20mm, with 10mm being the default.
*   **`load_body`**: This should be a boolean value; If set to true, the design needs to include the final\_body.stl from the filament control I linked to. If false it should just generate the block and holder.
*   The number of tubes, and thus holes, in the block should be customizable via a parameter. A good default would be 8, but if I'm using less than the full number of channels, I would only need a few holes. I want them equally spaced, with a default 10.5mm between each.

The design should consist of:
*   The tube holding **block**: A polygon with a number of holes for the tubes, which match the `tube_diameter` parameter, and are vertically aligned.
*   The **holder**:  A base with two side walls, and some edges to lock the block in place, but the edges need to still allow for removal of the block.

Could you please generate a set of parametric OpenSCAD files for this? I expect to need a main file to control the parameters, the block, the holder, and then a final file for the optional final\_body.stl from the filament control thingiverse link. Let me know if there's anything I've left out.

[Assistant]
mmu_ptfe.scad:
```
/*
  Quick release ptfe tube holder for "MMU2 filament control" by pmeyer
  https://www.thingiverse.com/thing:3184377

  If you have already printed the Final_body.stl, you can print the
  holder and super glue it to the Final_body. That's what I've done.
  If not, then you can leave load_body set to true, and it should be
  one solid piece.
*/

// Which one would you like to see?
part = "both"; //[block,holder,both]

// ptfe tube hole diameter
tube_diameter = 4.1; //[1:0.1:8]
// Outer width of holder. Should be 65.
width = 65; //[22:95.5]
// Height of holder. If too tall, it will prevent removing the block.
height = 31.5; //[23:0.1:35]
// The thickness of the block
thick = 10; //[2:20]

/* [Hidden] */
$fn=100;
// Merge the Final_body.stl mesh with the holder as one piece.
// Note: Customizer doesn't support importing stl files.
load_body = true; //[Yes,false]

module tubeHoles() {
  union() {
    translate([-1, 11.5, 31]) {
      // Note: Support up to 8 holes for no real reason
      for(i = [0 : 1 : 7]) {
        translate([0, 10.5*i])
        rotate([0,90])
        cylinder(h=thick+2, d=tube_diameter);
      }
    }
  }
}

// ptfe tube block
if (part == "block" || part == "both") {
  difference() {
    color("green") translate([0, width-5, 20]) rotate([90,0]) linear_extrude(width-10) polygon([
      [0,0],
      [thick-2, 0],
      [thick, 2],
      [thick, 11.5+tube_diameter],
      [2, 31.5],
      [0, 31.5]]);
    tubeHoles();
  }
}

union() {
  // holder for the tube block
  if (part == "holder" || part == "both") {
    // base
    cube([thick+5, width, 20]);
    // side walls
    cube([thick+5, 4.5, height]);
    translate([0, width-4.5,0]) cube([thick+5, 4.5, height]);
    // Edge that locks the block in
    translate([thick+0.5, 0]) {
      translate([0, 4.5]) cube([4.5, 2, height]);
      translate([0, width-2-4.5]) cube([4.5, 2, height]);
      //translate([0,0,20]) cube([4.5, width, 2]);
    }
  }

  if (load_body == true) {
    color("grey")
    rotate([0, 0, 180])
    translate([0, -65])
    import("Final_body.stl");
  }
}
```


[User]
Hi, I'm looking for an OpenSCAD design for a parametric cable clip/holder. I'd like it to have a circular opening for the cable, and a way to mount it with a screw. I need to be able to adjust the diameter of the opening, the thickness of the clip, and the size of the screw hole. Additionally, I'd like the option to have the opening split either on the front or on the side for easier cable insertion.

Could you create the OpenSCAD file for this? I'm thinking of something similar to what's described in this Thingiverse post: [https://www.thingiverse.com/thing:3258021](https://www.thingiverse.com/thing:3258021), but I need a parametric design from scratch rather than adjusting an existing file. I don't need any specific libraries to be used, but it would be nice if it were structured in a way that's easy to understand and modify.

[Assistant]
cable_holder.scad:
```
// cable_holder.scad
// library for parametric Cable Holder, Clip, Hook, Hanger, Loop
// Author: Tony Z. Tonchev, Michigan (USA)
// last update: November 2018

// (in mm)
Clip_Diameter = 30;

// (in mm)
Clip_Thickness = 10;

// #4-3mm, #6-3.5mm, #8-4mm, #9-4.5mm, #10-5mm (in mm)
Screw_Gauge = 4;

// (Front or Side)
Clip_Split = 0; // [0:Front,1:Side]

/* [Hidden] */

$fn=100;
TZT_Rad=Clip_Diameter/2;
TZT_Thk=Clip_Thickness;
TZT_Scr=Screw_Gauge;
TZT_Slt=Clip_Split;

difference () {
    TZT_CLIP();
    if ((TZT_Thk/8)<TZT_Scr) {
        TZT_SCREW();
    } else {
        translate ([0,0,TZT_Thk/4]) {
            TZT_SCREW();
        }
        translate ([0,0,-TZT_Thk/4]) {
            TZT_SCREW();
        }

    }
}

module TZT_SCREW () {
    translate ([-TZT_Rad*.98,0,0]) {
        rotate ([0,-90,0]) {
            cylinder (TZT_Scr*1.1,TZT_Scr,0);
            cylinder (TZT_Rad*.6,TZT_Scr/2,TZT_Scr/2);
            translate ([0,0,-TZT_Scr]) {
                cylinder (TZT_Scr*1.1,TZT_Scr,TZT_Scr);
            }
            if ((TZT_Slt==1)&&((TZT_Thk/3)>TZT_Scr)) {
                translate ([0,0,-TZT_Rad*3]) {
                    cylinder (TZT_Rad*3,TZT_Scr*.7,TZT_Scr*.7);
                }
            }
        }
    }
}

module TZT_CLIP () {
    difference () {
        union () {
            translate ([-TZT_Rad*1.1,0,0]) {
                difference () {
                    cube ([TZT_Rad*.6,TZT_Rad*2.2,TZT_Thk],true) ;
                    translate ([TZT_Rad*.2,TZT_Rad*1.07,0]) {
                        cylinder (TZT_Thk*1.1,TZT_Rad*.2,TZT_Rad*.2,true);
                    }
                    translate ([TZT_Rad*.2,-TZT_Rad*1.07,0]) {
                        cylinder (TZT_Thk*1.1,TZT_Rad*.2,TZT_Rad*.2,true);
                    }

                }
            }
            cylinder (TZT_Thk,TZT_Rad*1.2,TZT_Rad*1.2,true);
        }
        cylinder (TZT_Thk*4,TZT_Rad,TZT_Rad,true);

        if (TZT_Slt==1) {
            rotate ([0,0,90]) TZT_SPLIT();
        } else {
            TZT_SPLIT();
        }
    }
    module TZT_SPLIT () {
        translate ([TZT_Rad*1.1,0,0]) {
            for (TZT_i=[-30:30:30]) {
                rotate ([-TZT_Rad*10/TZT_Thk,0,TZT_i]) {
                    cube ([TZT_Rad,TZT_Rad*.1,TZT_Thk*2],true);
                }
            }
        }
    }
}
```


[User]
Hey, I'm looking to create a customizable picture frame using OpenSCAD. I've got some specific requirements, and I'd like the design to be parametric so I can easily adjust it later.

Here's what I need:

**Overall Structure:**

*   The frame should accommodate a rectangular photo.
*   It should have a main frame component and a top edge piece that snaps onto the main frame.
*   I'd like to have the option to add supports for the opening. This support design is a hexagonal pattern.

**Parameters:**

I'd need to be able to adjust the following parameters:

*   `wp`: Width of the photo.
*   `hp`: Height of the photo.
*   `tp`: Thickness of the photo.
*   `wt`: Wall thickness of the frame.
*   `bt`: Border thickness of the frame.
*   `iso`: Boolean value (0 or 1) to enable/disable supports for the opening.
*   `sr`: Radius for support hexagons if `iso` is enabled.
*   `ss`: Shift amount for support hexagons if `iso` is enabled.

**Specific Design Details:**

*   The frame should have a base, and an angled face that can hold the picture, with the angled face having a cutout for the picture.
*   The top edge piece needs to fit snugly over the main frame, locking it and the photo in place.
*   The support pattern (if `iso=1`) should use a hexagonal pattern on an angled surface, with the hexagons cut out of the material, that is, a negative space.
*   The frame should have an additional cutout for inserting and removing the photo.
*   There should be some support structures along the sides of the picture to make the frame more sturdy.
*  A small decorative circle should be cut out of the back of the main frame.

**Modules & Options:**

*   I'd like a module called `HexPatternV2` to generate the hexagonal support pattern. This module should take dimensions, radius, wall thickness, an invert flag and an angling flag. It should use an intersection of a cube and a cylinder array. The module should be able to use a negative cylinder array to form the negative space.
*   I'd like a `Frame` module to create the main frame with all the cutouts and angles, including the support structures, small circle cut-out, and hexagonal support structure.
*  I'd like a `TopEdge` module to generate the top edge piece.
*  I'd also like different render options that allow me to view the whole assembled piece, the main frame only, the top edge only, and finally, the frame and top edge separate for printing.

**Libraries:**

*   I am not using any libraries.

Could you provide the OpenSCAD code for this? Thanks!

[Assistant]
Bilderrahmen_V2.scad:
```
// -----------------------------------------------------
// Bilderrahmen V2
//
//  Detlev Ahlgrimm, 11.2018
// -----------------------------------------------------

// what to render
part=0;   // [0:assembled, 1:print, 2:frame only, 3:top edge only]

// width of photo
wp=130;   // [70:250]

// height of photo
hp=180;   // [70:250]

// thickness of photo
tp=1;     // [0.5:0.1:3]

// wall thickness
wt=0.8;   // [0.5:0.05:3]

// border thickness
bt=5;     // [1:0.1:10]

// support for opening
iso=0;    // [0:no support, 1:support]

// radius for support
sr=18;    // [10:0.5:30]

// shift for support
ss=0;     // [0:50]

/* [Hidden] */
$fn=100;
inner=[wp, hp, tp];

// -----------------------------------------------------
//    v : Abmessungs-Vektor
//    r : Radius der Aussparungen
//    w : Wandstaerke zwischen den Aussparungen
//    i : invertieren (true:Sechseck-Umrandungen, false:Sechsecke)
//    a : anschraegen 45 Grad (bei true)
// -----------------------------------------------------
module HexPatternV2(v, r=10, w=1, i=true, a=false) {
  dx=r*1.5;
  dy=r*0.866025;      // 0.866025=sqrt(3)/2
  sy=(i) ? -0.1 : 0;
  az=(i) ? 0.2 : 0;
  r1=(a) ? r-w/2-(v.z+0.2) : r-w/2;

  intersection() {
    difference() {
      if(i) cube(v);
      for(yi=[0:1:v.y/dy+1]) {
        for(xi=[0:1:v.x/(2*dx)+1]) {
          if((yi%2)==1) {
            translate([xi*2*dx+dx, yi*dy, sy]) cylinder(r1=r1, r2=r-w/2, h=v.z+az, $fn=6);
          } else {
            translate([xi*2*dx, yi*dy, sy]) cylinder(r1=r1, r2=r-w/2, h=v.z+az, $fn=6);
          }
        }
      }
    }
    if(!i) cube(v);
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module Frame() {
  rotate([75, 0, 0])
  difference() {
    cube([inner.x+2*wt, inner.y+5*wt, inner.z+2*wt]);
    translate([wt, 3*wt, wt]) cube([inner.x, inner.y+3*wt, inner.z]);
    translate([wt+bt, wt+bt, wt+inner.z-0.01]) cube([inner.x-2*bt, inner.y+3*wt, 2*wt]);
    rotate([-75-45, 0, 0]) translate([-0.1, -2*(wt+inner.z), -(wt+inner.z)]) cube([inner.x+2*wt+0.2, 2*(wt+inner.z), wt+inner.z]);
    translate([(inner.x+2*wt)/2, (inner.y+5*wt)/2, -0.1]) scale([(inner.x-20)/2, (inner.y-20)/2, 1]) cylinder(r=1, h=wt+0.2);
  }

  difference() {
    cube([inner.x+2*wt, inner.y/3, wt]);
    translate([(inner.x+2*wt)/2, inner.y/3, -0.1]) scale([1, 0.7, 1]) cylinder(d=inner.x, h=wt+0.2);
  }
  if(iso==1) {
    rotate([75, 0, 0]) intersection() {
      translate([-ss, inner.y+5*wt, 0]) rotate([0, 0, -90]) HexPatternV2([inner.y+5*wt, (inner.x+2*wt)+50, wt-0.01], sr, 4);
      cube([inner.x+2*wt, inner.y+5*wt, inner.z+2*wt]);
    }
  }
  cube([inner.x+2*wt, 1, wt]);

  difference() {
    for(x=[0, inner.x+wt])
      translate([x, 0, 0]) hull() {
        cube([wt, inner.y/3, wt]);
        rotate([75, 0, 0]) cube([wt, inner.y, 0.01]);
      }
    hull() {
      translate([-0.1, inner.y/3, inner.y/6+wt]) rotate([0, 90, 0]) cylinder(r=inner.y/6, h=inner.x+4*wt+0.2);
      translate([-0.1, inner.y/3+inner.y/15, inner.y]) rotate([0, 90, 0]) cylinder(r=inner.y/6, h=inner.x+4*wt+0.2);
    }
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module TopEdge() {
  translate([wt+bt+0.3, 5*wt+inner.y-bt, wt+inner.z-0.2]) cube([inner.x-2*bt-0.6, bt+wt, wt+0.2]);
  translate([3*wt, 3*wt+inner.y, wt+0.1]) cube([inner.x-4*wt, 2*wt, inner.z-0.2]);
  translate([0, inner.y+5*wt+0.1, -wt-0.2]) cube([inner.x+2*wt, wt, inner.z+3*wt+0.2]);
  translate([0, inner.y+wt, -wt-0.2]) cube([inner.x+2*wt, 4*wt+0.2, wt]);
}

if(part==0) {
  Frame();
  rotate([75, 0, 0]) TopEdge();
} else if(part==1) {
  Frame();
  translate([0, -10*wt, 0]) rotate([-90, 0, 0]) translate([0, -(6*wt+inner.y+0.1), wt+0.2]) TopEdge();
} else if(part==2) {
  Frame();
} else if(part==3) {
  translate([0, -10*wt, 0]) rotate([-90, 0, 0]) translate([0, -(5*wt+inner.y+0.1), wt+0.2]) TopEdge();
}

```


[User]
Hey, I'm looking to create a parametric filament sample tag in OpenSCAD. I'd like the tag to be a rectangular prism with rounded ends, and a hole for hanging it. On the tag I need three lines of text: one that displays the filament type, one that is a label for the manufacturer (line 1), and one that is a label for the color or variant (line 2). I need to be able to control the following parameters:

*   **length:** The length of the tag.
*   **width:** The width of the tag.
*   **thickness:** The thickness of the tag.
*   **type:** The filament type text (e.g. "PLA", "PETG").
*   **line1\_text:** The text for the first line (e.g. "Hatchbox").
*   **line1\_size:** The font size for the first line of text.
*   **line2\_text:** The text for the second line (e.g. "Red").
*   **line2\_size:** The font size for the second line of text.
*   **font\_family:** The font family for all text (e.g. "Arial", "Times New Roman").
*   **font\_weight:** The font weight for all text (e.g. "bold", "normal").
*   **spacing:** A parameter that affects how the two main lines of text (line 1 and line 2) are vertically positioned; can be either 1 or 2, with 1 being for shorter text and 2 for longer text.
*   **line1\_y\_tweak:** A manual adjustment for the Y-axis position of the first line of text.
*   **line2\_y\_tweak:** A manual adjustment for the Y-axis position of the second line of text.

The tag itself should be a rectangular prism with a cylinder subtracted from each end to make it look rounded, and it should have a hole in the middle of one end. All three lines of text should be extruded outwards from the main tag shape on top of the face of the tag, not cut into it. They should be centered along the length of the tag, and the text type should appear on the left side of the tag and oriented so that it's read from the side and not the top. The first line (line 1) should appear above the second line (line 2), and the vertical spacing should change depending on the "spacing" variable described above, and I need to be able to adjust their positions with the "line1\_y\_tweak" and "line2\_y\_tweak" variables.

Could you please generate the OpenSCAD code for this? I'm not using any libraries.

[Assistant]
filament_sample_tag.scad:
```
// All measurements are in mm.

// The length of the tag.
length = 60;

// The width of the tag.
width = 30;

// The thickness of the tag.
thickness = 3;

// The filament type.
type = "PLA";

// The line 1 text.
line1_text = "Hatchbox";

// The line 1 text size.
line1_size = 8;

// The line 2 text.
line2_text = "Green";

// The line 2 text size.
line2_size = 13;

// The font. Select Help -> Font List for possible values.
font_family = "Arial";

// The font weight.
font_weight = "bold";

// The line spacing method. 1 usually works better when you have short strings in a big font. 2 usually works better when you have long strings in a small font.
spacing = 1;

// The line 1 Y axis tweak. Used to fine-tune the text position. Usually not needed.
line1_y_tweak = 0;

// The line 1 Y axis tweak. Used to fine-tune the text position. Usually not needed.
line2_y_tweak = 0;

line1_y = (spacing == 1 ? (width * 0.75) : (width - (13 - line1_size))) + line1_y_tweak;
line1_valign = spacing == 1 ? "center" : "top";
line2_y = (spacing == 1 ? (width * 0.25) : (13 - line2_size)) + line2_y_tweak;
line2_valign = spacing == 1 ? "center" : "bottom";

font = str(font_family, ":style=", font_weight);

union() {
    cube([length, width, thickness]);

    difference() {
        translate([0, width / 2, 0])
            cylinder(thickness, width / 2, width / 2, $fn = 75);
        translate([-width / 4, width / 2, -1])
            cylinder(thickness+ 2, 4, 4, $fn = 50);
    }

    translate([1, width / 2, thickness])
        rotate([0, 0, 90])
        linear_extrude(1)
        text(
            text=type,
            size=7,
            font=font,
            halign="center",
            valign="center"
        );

    translate([length / 2 + 2, line1_y, thickness])
        linear_extrude(1)
        text(
            text=line1_text,
            size=line1_size,
            font=font,
            halign="center",
            valign=line1_valign
        );

    translate([length / 2 + 2, line2_y, thickness])
        linear_extrude(1)
        text(
            text=line2_text,
            size=line2_size,
            font=font,
            halign="center",
            valign=line2_valign
        );
}

```