[User]
Hey, I'm looking to create a customizable mini-stand, like the "Mini Floor Stands" on Thingiverse by muzz64, but I want it to be completely parametric in OpenSCAD. I've seen a version of this in OpenSCAD, but I want my own from scratch.

Essentially, it's a small stand made of two panels connected by a hinge, with a supporting arm that can lock the stand open. I need to be able to customize things like the text on the frame, the dimensions of the stand itself, clearances for printing, and some other parameters. I want to make sure it's all driven by variables so I can easily adjust it.

Here's a breakdown of what I need:

**Basic Parameters:**

*   **Clearance:** A variable to control the clearance between parts (e.g., 0.5mm, but I want to be able to change it). I'd like to control it with a range like `[0.0:0.1:1]`
*   **Text:** Four separate lines of text that can be customized, and scaled. I would like to set the scale, the vertical offset, and the vertical line height between the lines.
*   **Font:**  The font used for the text should be selectable. I want to be able to specify it like `"DejaVu Sans:style=Condensed Bold"`
*   **Precision:** I want to set `$fn` to control the resolution of curves

**Stand Dimensions:**

*   **Stand Width:** The overall width of the stand.
*   **Stand Height:** The overall height of the stand.
*   **Stand Depth:** The thickness of the panels.
*   **Stand Corner Radius:** The radius of the rounded corners.

**Handle:**

*   **Handle Width:** The width of the handle cutout on each panel.
*   **Handle Height:** The height of the handle cutout.
*   **Handle Offset:** The vertical offset of the handle cutout from the bottom of the panel.

**Frame (for text):**

*   **Frame Width:** The width of the frame around the text.
*   **Frame Height:** The height of the frame around the text.
*   **Frame Border:** The thickness of the frame's border.
*   **Frame Depth:** The depth of the frame extrusion.
*   **Frame Offset:** The vertical offset of the frame from the center of the panel.

**Legs:**

*   **Leg Length:** The overall length of the legs.
*   **Leg Width:** The width of the legs.
*  **Leg Cut Angle:** The angle at which the legs are cut.

**Hinge:**

*   **Hinge Side Width:** The width of the side pieces of the hinge.
*   **Hinge Middle Width:** The width of the middle piece of the hinge.
*   **Hinge Length:** The length of the hinge.
*	**Hinge Cone Angle**: The angle for the cone-shaped hinge joints.

**Arm (support):**

*   **Arm Length:** The length of the arm that supports the stand.
*   **Arm Width:** The width of the arm.
*   **Arm Thickness:** The thickness of the arm.

**Other:**
*   **Minimum Thickness:** a minimum thickness for internal components.
*   **Diff Extra**: This is used in the difference operations to add extra material when creating the cutouts. This value is usually something very small like `0.1`

I know that's a lot, but I think breaking it down like this should make it easier to manage. It would be great if you could provide a well-structured OpenSCAD file, or multiple files if it makes sense to organize it that way. I want to be able to just change variables at the top of the file and have the design update.  I don't want to rely on any specific external libraries like BOSL or MCAD for this, just the standard OpenSCAD functions.

Thanks a bunch!

[Assistant]
mini-stand.scad:
```
/*
 * =====================================
 *   This is public Domain Code
 *   Contributed by: Gael Lafond
 *   24 November 2017
 * =====================================
 *
 * Mini-stand, OpenSCAD version from muzz64:
 *   https://www.thingiverse.com/thing:1977933
 *
 * Re-modeled with muzz64 permission.
 *
 * Units are in millimetres.
 */

/* [Basic] */

// The original model have a clearance of 0.5mm. A clearance of 0.2mm works very well with precise printers like the Prusa i3.
clearance = 0.5; // [0.0:0.1:1]

// First line of text
string1 = "DO";

// Second line of text
string2 = "NOT";

// Third line of text
string3 = "TURN";

// Forth line of text
string4 = "OFF";

// Scale of the text, adjust to make it fit
textScale = 0.7; // [0.1:0.05:1]

// Move the text down
textOffset = -19; // [-40:1:40]

// Move the text down
textLineHeight = 16; // [1:1:50]

/* [Advanced] */

// Precision
$fn = 40;

// Font used for the text. For a list of available fonts, use: Help > Font List
font = "DejaVu Sans:style=Condensed Bold";

standWidth = 40;
standHeight = 62;
standDepth = 5;
standCornerRadius = 2.5;

standHandleWidth = 20;
standHandleHeight = 3.5;
standHandleOffset = 3.5;

frameWidth = 36;
frameHeight = 51;
frameBorder = 0.8;
frameDepth = 1.2;
frameOffset = 3.6;

legLength = 7.5;
legWidth = 10;
legCutAngle = 23; // TODO Calculate from "arm" length

hingeSideWidth = 7.6;
hingeMiddleWidth = 11;
hingeLength = 7.5;

// The arm underneath, used to lock the mini-stand
armLength = 40;
armWidth = 4;
armThickness = 3;

minimumThickness = 0.4;
hingeConeAngle = 60;

// Extra padding to help when evaluating 3D difference.
diffExtra = 0.1;

// Variables calculated to simplify the code
// Hinge angle
hingeTheta = 90 - hingeConeAngle;
// hingeOffset is the distance between the tip of the cone and the tip of the conic hole
hingeOffset = clearance / sin(hingeTheta);

hingeConeHoleRadius = standDepth/2 - minimumThickness;
hingeConeHoleLength = hingeConeHoleRadius / tan(hingeTheta);

hingeConeLength = hingeConeHoleLength + clearance - hingeOffset;
hingeConeRadius = hingeConeLength * hingeConeHoleRadius / hingeConeHoleLength;

// Clearance on both side
armHoleWidth = armWidth + 2*clearance;
armHoleLength = armLength + 2*clearance;
// Increase clearance on top of the arm (we don't want it to get stuck inside)
armHoleDepth = armThickness + 2*clearance;

armHingeConeHoleRadius = armThickness/2 - minimumThickness;
armHingeConeHoleLength = armHingeConeHoleRadius / tan(hingeTheta);

armHingeConeLength = armHingeConeHoleLength + clearance - hingeOffset;
armHingeConeRadius = armHingeConeLength * armHingeConeHoleRadius / armHingeConeHoleLength;

legInnerLength = legLength - (standDepth / tan(90-legCutAngle));
armEndRadius = armThickness / 2;

// Debuging
*color([0, 0, 1, 0.2]) {
	//import("../orig/blank.stl");
	import("../orig/do_not_turn_off.stl");
}

// Debuging
*difference() {
	miniStand();
	translate([0, 50, 0]) {
		cube([200, 100, 100], center=true);
	}
}

miniStand();

module miniStand() {
	translate([hingeLength, standWidth/2, 0]) {
		rotate([0, 0, -90]) {
			difference() {
				panel();
				armHole();
			}

			arm();
		}
	}

	translate([-hingeLength, -standWidth/2, 0]) {
		rotate([0, 0, 90]) {
			difference() {
				panel();
				armLockHole();
			}
		}
	}

	color([1, 0, 0]) {
		hinge();
	}
}

// One of the 2 side of the stand, without the hinge, without hole at the back.
module panel() {
	color([1, 0, 0]) {
		linear_extrude(standDepth) {
			difference() {
				union() {
					// Panel with holes for the rounded corner
					translate([0, standCornerRadius]) {
						square([standWidth, standHeight - standCornerRadius]);
					}
					translate([standCornerRadius, 0]) {
						square([standWidth - (standCornerRadius * 2), standCornerRadius]);
					}

					// Rounded corner
					translate([standCornerRadius, standCornerRadius]) {
						circle(r = standCornerRadius);
					}
					translate([standWidth - standCornerRadius, standCornerRadius]) {
						circle(r = standCornerRadius);
					}
				}

				// Handle (hole)
				translate([(standWidth - standHandleWidth) / 2, standHandleOffset]) {
					square([standHandleWidth, standHandleHeight]);
				}
			}
		}

		// Legs
		translate([0, standHeight, 0]) {
			leg();
		}
		translate([standWidth - legWidth, standHeight, 0]) {
			leg();
		}
	}

	// Frame
	color([1, 1, 1]) {
		translate([
			standWidth / 2,
			standHeight / 2 + frameOffset,
			standDepth
		]) {
			rotate([0, 0, 180]) {
				frame();
			}
		}
	}
}

// The frame around the text
module frame() {
	linear_extrude(frameDepth) {
		difference() {
			square([frameWidth, frameHeight], center = true);
			square([
				frameWidth - (frameBorder * 2),
				frameHeight - (frameBorder * 2)
			], center = true);
		}

		scale([textScale, textScale]) {
			translate([0, -textOffset]) {
				text(string1, font = font, halign = "center");
			}
			translate([0, -textOffset - textLineHeight]) {
				text(string2, font = font, halign = "center");
			}
			translate([0, -textOffset - textLineHeight*2]) {
				text(string3, font = font, halign = "center");
			}
			translate([0, -textOffset - textLineHeight*3]) {
				text(string4, font = font, halign = "center");
			}
		}
	}
}

// The legs at the bottom, cut in an angle
module leg() {
	rotate([90, 0, 90]) {
		linear_extrude(legWidth) {
			polygon([
				[0, 0],
				[legInnerLength, 0],
				[legLength, standDepth],
				[0, standDepth]
			]);
		}
	}
}

module hinge() {
	// Middle part
	difference() {
		translate([0, hingeMiddleWidth/2, standDepth/2]) {
			rotate([90, 0, 0]) {
				linear_extrude(hingeMiddleWidth) {
					hinge2DProfile();
				}
			}
		}
		// Conic holes
		translate([0, hingeMiddleWidth/2, standDepth/2]) {
			rotate([90, 0, 0]) {
				cylinder(r1 = hingeConeHoleRadius, r2 = 0, h = hingeConeHoleLength);
			}
		}
		translate([0, -hingeMiddleWidth/2, standDepth/2]) {
			rotate([-90, 0, 0]) {
				cylinder(r1 = hingeConeHoleRadius, r2 = 0, h = hingeConeHoleLength);
			}
		}
	}

	// Side parts
	translate([0, hingeMiddleWidth/2 + hingeSideWidth + clearance, standDepth/2]) {
		rotate([90, 180, 0]) {
			linear_extrude(hingeSideWidth) {
				hinge2DProfile();
			}
		}
	}
	translate([0, hingeMiddleWidth/2 + clearance, standDepth/2]) {
		rotate([90, 0, 0]) {
			cylinder(r1 = hingeConeRadius, r2 = 0, h = hingeConeLength);
		}
	}

	translate([0, -hingeMiddleWidth/2 - clearance, standDepth/2]) {
		rotate([90, 180, 0]) {
			linear_extrude(hingeSideWidth) {
				hinge2DProfile();
			}
		}
	}
	translate([0, -hingeMiddleWidth/2 - clearance, standDepth/2]) {
		rotate([-90, 0, 0]) {
			cylinder(r1 = hingeConeRadius, r2 = 0, h = hingeConeLength);
		}
	}
}

module hinge2DProfile() {
	// NOTE: Parameter "d" (diameter) is only available from OpenSCAD 2014.03
	circle(r = standDepth/2);
	translate([0, -standDepth/2]) {
		square([hingeLength, standDepth]);
	}
}

module arm() {
	translate([
		(standWidth - armWidth) / 2,
		(standHeight - armLength) / 2,
		0
	]) {
		difference() {
			cube([armWidth, armLength - armEndRadius, armThickness]);
			// Hole in the arm, to lock it in place
			translate([-diffExtra, armThickness/2, armThickness/2]) {
				cube([
					armWidth + 2*diffExtra,
					armThickness/2 + 2*clearance,
					armThickness/2 + diffExtra
				]);
			}
		}
		translate([0, armLength - armEndRadius, armEndRadius]) {
			rotate([0, 90, 0]) {
				cylinder(r = armEndRadius, h = armWidth);
			}
		}
	}

	// Arm hinges
	translate([
		(standWidth - armWidth) / 2 + armWidth,
		(standHeight - armLength) / 2 + armLength - armEndRadius,
		armEndRadius
	]) {
		rotate([0, 90, 0]) {
			cylinder(r1 = armHingeConeRadius, r2 = 0, h = armHingeConeLength);
		}
	}

	translate([
		(standWidth - armWidth) / 2,
		(standHeight - armLength) / 2 + armLength - armEndRadius,
		armEndRadius
	]) {
		rotate([0, -90, 0]) {
			cylinder(r1 = armHingeConeRadius, r2 = 0, h = armHingeConeLength);
		}
	}
}

// Hole under the panel to store the arm when not in use.
module armHole() {
	// Extra space to allow the arm to get out of its hole
	armRadius = armLength - armEndRadius;
	armHalfDepth = armThickness / 2;
	armHoleGap = sqrt(armRadius*armRadius + armHalfDepth*armHalfDepth) - armRadius;

	translate([
		(standWidth - armHoleWidth) / 2,
		(standHeight - armHoleLength) / 2 - armHoleGap,
		-diffExtra
	]) {
		cube([armHoleWidth, armHoleLength + armHoleGap, armHoleDepth + diffExtra]);
	}

	// Holes for the arm hinges
	translate([
		(standWidth - armHoleWidth) / 2 + armHoleWidth,
		(standHeight - armLength) / 2 + armLength - armEndRadius,
		armEndRadius
	]) {
		rotate([0, 90, 0]) {
			cylinder(r1 = armHingeConeHoleRadius, r2 = 0, h = armHingeConeHoleLength);
		}
	}

	translate([
		(standWidth - armHoleWidth) / 2,
		(standHeight - armLength) / 2 + armLength - armEndRadius,
		armEndRadius
	]) {
		rotate([0, -90, 0]) {
			cylinder(r1 = armHingeConeHoleRadius, r2 = 0, h = armHingeConeHoleLength);
		}
	}
}

// Hole under the other panel, use to lock the arm in place.
module armLockHole() {
	armLockHoleWidth = armHoleWidth;
	armLockHoleHeight = armThickness + 4*clearance;

	// Deep hole to allow the arm to slide in place
	armLockHoleDepth = 1.2 * armThickness + diffExtra;

	armLockHeight = armThickness/2;
	armLockThickness = armThickness/2 - clearance;

	translate([
		(standWidth - armLockHoleWidth) / 2,
		(standHeight - armLength) / 2 + armLength - armLockHoleHeight,
		-diffExtra
	]) {
		difference() {
			cube([armLockHoleWidth, armLockHoleHeight, armLockHoleDepth]);
			translate([0, -diffExtra, -diffExtra]) {
				cube([
					armLockHoleWidth + diffExtra,
					armLockHeight + diffExtra,
					armLockThickness + 2*diffExtra
				]);
			}
		}
		translate([-armLockHoleWidth, 0, 0]) {
			cube([
				armLockHoleWidth,
				armLockHoleHeight,
				armLockHoleDepth
			]);
		}
	}
}

```


[User]
I'd like to design a parametric Christmas star generator in OpenSCAD, similar to the one I found online. I need it to be highly customizable, allowing for both 3D printing and 2D laser cutting. Here are the features and parameters I want to control:

**Overall Structure:**

*   **Projection Mode:** A flag to switch between a 3D model for printing and a 2D projection for laser cutting. I'll need a `proj_flag` variable, with `0` representing 3D and `1` representing 2D.
*   **Star Height:** The overall height/thickness of the star, controlled by a `ht` variable.
*   **Stripe Width:** The width of the individual bars/stripes that make up the star, controlled by a `wd` variable.
*   **Stacking Levels:** The number of stacked sub-stars, with options for single, double, triple, quadruple, quintuple, sextuple and septuple layers or a combination, controlled by a `stack` variable with values ranging from 0 to 7.
*   **Chamfer:** The height of the chamfer on the star's edges, adjustable using a `chamfer` variable.
*   **Comet Tail:** An option to include a comet tail, controlled by a `sch_flag` variable (`0` for no tail, `1` for tail).
*   **Tail Bar Width:** If the tail is enabled, the width of the bars in the tail using a `wd_schweif` variable.
*   **Fixing Holes:** A flag to add fixing holes to the star for hanging, controlled by a `fix_flag` variable (`0` for no holes, `1` for holes).

**Star Sizes:**

I want the diameter of each sub-star to be customizable. Please use these variables:

*   `DStar1` (smallest)
*   `DStar2`
*   `DStar3`
*   `DStar4`
*   `DStar5`
*  `DStar6`
*   `DStar7` (largest)

**Specific Modules:**

*   I'll need a module to create the basic "positive" star shape, including the chamfer, let's call this `cyl_pchamf`. This should create a cylinder with chamfered edges (outer cylinder).
*   A second module should create the "negative" space by subtracting a smaller cylinder, with a chamfer. Let's call this `cyl_nchamf`.
*   I'll need a `stern_p` module that uses `cyl_pchamf` to create a star shape based on a given radius.
*   Similarly, I need a `stern_n` module based on `cyl_nchamf` to define the negative space within the star.
*   Modules to generate each of the stacking levels: `einfachstern`, `doppelstern`, `dreifachstern`, `vierfachstern`, `fuenffachstern`, `sechsfachstern`, and `siebenfachstern`. Each should build on the previous, including the holes if `fix_flag` is enabled.
*   A `schweifbogen` module to create a section of the comet tail.
*   A `schweif` module to assemble the comet tail using multiple `schweifbogen` elements and add additional parts to the tail.
*   A `kombistern` module to generate the combined star with smaller stars around the edge.

**Additional Notes:**

*   The negative space cylinders should be slightly scaled up (by 1.01) in the z-direction for better visibility in the preview.
*   The fixing holes should be cylinders centered and passing through the entire star (height should be greater than the object).
*   For the `kombistern` it should use  `fuenffachstern` as a basis with multiple scaled `doppelstern` and `dreifachstern` instances rotated around it.
*   The comet tail should adapt based on the overall star size.

I don't need any fancy libraries, just standard OpenSCAD. This should give me complete control over the stars. Let me know if you have any questions.

[Assistant]
2019_my_stern_ballcube_v4.scad:
```
// Weihnachtssterne 2014 - 2019
// (C) Henning und Daniel Stöcklein
// Version 15.12.2019

// Projektion for Laser or 3D-Objekt for Printer
proj_flag = 0 ; // [0:3D-Model, 1:2D-Projection]

// Height of star
ht = 1.0 ; // [0.5:0.1:3]

// width of stripes
wd = 2.4 ; // [1:0.2:5]

// Number of stacked sub-stars
stack = 0 ; // [0:7]

// Chamfer height
chamfer = 0.5 ; // [0:0.1:0.7]

// Schweifstern Y/N
sch_flag = 0 ; // [0:no Tail, 1:Comet Tail]

// Width of Bars
wd_schweif = 1.6 ; // [1.4:0.1:2.5]

// Fixing holes Y/N
fix_flag = 0 ; // [0:no holes, 1:holes]

DStar1 = 5.0 ; // [4:0.1:6]
DStar2 = 9.5 ; // [8:0.1:12]
DStar3 = 17.0 ; // [15:0.1:20]
DStar4 = 21.0 ; // [19:0.1:23]
DStar5 = 27.4 ; // [25:0.1:30]
DStar6 = 34.0 ; // [32:0.1:36]
DStar7 = 42.0 ; // [39:0.1:45]

//******************** Objektauswahl ****************************
if (proj_flag == 1)
{
  projection (cut=true)
  {
     if (stack == 1) einfachstern();
     if (stack == 2) doppelstern() ;
     if (stack == 3) dreifachstern() ;
     if (stack == 4) vierfachstern() ;
     if (stack == 5) fuenffachstern() ;
     if (stack == 6) sechsfachstern() ;
     if (stack == 7) siebenfachstern() ;
     if (stack == 0) kombistern() ;
        if (sch_flag == 1)
          rotate ([0,0,3]) translate ([21, 2.5, 0]) scale ([0.87,0.82,1]) schweif() ;
  }
}
else
{
     if (stack == 1) einfachstern();
     if (stack == 2) doppelstern() ;
     if (stack == 3) dreifachstern() ;
     if (stack == 4) vierfachstern() ;
     if (stack == 5) fuenffachstern() ;
     if (stack == 6) sechsfachstern() ;
     if (stack == 7) siebenfachstern() ;
     if (stack == 0) kombistern() ;
        if (sch_flag == 1)
           rotate ([0,0,3]) translate ([21, 2.5, 0]) scale ([0.87,0.82,1]) schweif() ;
}

//******************************************
// Chamfered Cylinder Submodule (centered)
//******************************************
module cyl_pchamf (rad, ht, pfn, rchamf)
{
    $fn = pfn;
    translate ([0,0,-ht/2+rchamf/2]) cylinder (r2=rad, r1=rad-rchamf, h=rchamf, center=true) ;
    translate ([0,0,0]) cylinder (r=rad, h=ht-2*rchamf+0.01, center=true) ;
    translate ([0,0,ht/2-rchamf/2]) cylinder (r1=rad, r2=rad-rchamf, h=rchamf, center=true) ;
}

module cyl_nchamf (rad, ht, pfn, rchamf)
{
    $fn = pfn;
    translate ([0,0,-ht/2+rchamf/2]) cylinder (r1=rad, r2=rad-rchamf, h=rchamf, center=true) ;
    translate ([0,0,0]) cylinder (r=rad, h=ht-2*rchamf+0.01, center=true) ;
    translate ([0,0,ht/2-rchamf/2]) cylinder (r2=rad, r1=rad-rchamf, h=rchamf, center=true) ;
}

//******************************************
module stern_p (radius)
{
   rotate ([0,0,0]) cyl_pchamf (radius, ht, 3, chamfer);
   rotate ([0,0,60]) cyl_pchamf (radius, ht, 3, chamfer);
}

module stern_n (radius)
{
   rotate ([0,0,0]) cyl_nchamf (radius, ht, 3, chamfer);
   rotate ([0,0,60]) cyl_nchamf (radius, ht, 3, chamfer);
}

module einfachstern()
{
  difference()
  {
    stern_p(DStar3);
    scale ([1,1,1.01]) stern_n(DStar3-wd);
    if (fix_flag == 1)
      rotate ([0,0,30]) translate ([0,DStar3-2.7,0])
        cylinder (r=0.6, h=10, center=true, $fn=20) ;
  }
}

module doppelstern()
{
  difference()
  {
    stern_p(DStar3);
    scale ([1,1,1.01]) stern_n(DStar3-wd);
    if (fix_flag == 1) rotate ([0,0,30]) translate ([0,DStar3-2.7,0]) cylinder (r=0.6, h=10, center=true, $fn=20) ;
  }
  difference()
  {
    rotate ([0,0,30]) stern_p(DStar2);
    rotate ([0,0,30]) scale ([1,1,1.01]) stern_n(DStar2-wd);
  }
}

module dreifachstern()
{
  doppelstern() ;

  difference()
  {
    stern_p(DStar1);
    scale ([1,1,1.01]) stern_n(DStar1-wd);
  }
}

module vierfachstern()
{
  dreifachstern() ;

  difference()
  {
    rotate ([0,0,30]) stern_p(DStar4);
    rotate ([0,0,30]) scale ([1,1,1.01]) stern_n(DStar4-wd);
    if (fix_flag == 1) rotate ([0,0,00]) translate ([0,DStar4-2.0,0]) cylinder (r=0.5, h=10, center=true, $fn=20) ;
  }
}

module fuenffachstern()
{
  vierfachstern() ;

  difference()
  {
    stern_p (DStar5);
    scale ([1,1,1.01]) stern_n (DStar5-wd);
    if (fix_flag == 1)
    {
      rotate ([0,0,30]) translate ([0,DStar5-3.0,0])
        cylinder (r=0.5, h=10, center=true, $fn=20) ;
      rotate ([0,0,30]) translate ([0,-DStar5+3.0,0])
        cylinder (r=0.5, h=10, center=true, $fn=20) ;
    }
  }
}

module sechsfachstern()
{
  fuenffachstern() ;

  difference()
  {
    rotate ([0,0,30]) stern_p (DStar6);
    scale ([1,1,1.01]) rotate ([0,0,30]) stern_n (DStar6-wd);
  }
}

module siebenfachstern()
{
  sechsfachstern() ;

  difference()
  {
    stern_p (DStar7);
    scale ([1,1,1.01]) stern_n (DStar7-wd);
  }
}

module schweifbogen()
{
  translate ([0,    0,0]) rotate ([0,0,0]) cube ([20, wd_schweif, ht], center=true);
  translate ([19,  -0.8,0]) rotate ([0,0,-5]) cube ([20, wd_schweif, ht], center=true);
  translate ([38.5,-3.4,0]) rotate ([0,0,-10]) cube ([20, wd_schweif, ht], center=true);
  translate ([57,  -7.5,0]) rotate ([0,0,-15]) cube ([20, wd_schweif, ht], center=true);
}

module schweif()
{
  difference() {
    union() {
      // 3 Hauptstrahlen
      translate ([0,0,0]) rotate ([0,0,0]) schweifbogen() ;
      translate ([-1,-4.5,0]) rotate ([0,0,-4.0]) scale ([0.81,1,1]) schweifbogen() ;
      translate ([-2,-9,0]) rotate ([0,0,-14]) schweifbogen() ;

      // Zackenschwanz
      translate ([57,  -9.5,0]) rotate ([0,0,-4.1]) cube ([20, wd_schweif, ht], center=true);
      translate ([55.6,  -13.2,0]) rotate ([0,0,-26.8]) cube ([20, wd_schweif, ht], center=true);
      translate ([55,  -16.7,0]) rotate ([0,0,-6]) cube ([20, wd_schweif, ht], center=true);
      translate ([54,  -19.5,0]) rotate ([0,0,-25]) cube ([20, wd_schweif, ht], center=true);
      translate ([53.5, -22.7,0]) rotate ([0,0,-6.0]) cube ([20, wd_schweif, ht], center=true);
      translate ([52.0,  -28.2,0]) rotate ([0,0,-39.3]) cube ([21, wd_schweif, ht], center=true);

      // Befestigungsloch
      if (fix_flag == 1) translate ([0,0,0]) cylinder (r=1.4, h=ht, $fn=20, center=true) ;
    }
    translate ([0,0,0]) cylinder (r=0.6, h=ht, $fn=20, center=true) ;
  }
}

module kombistern()
{
    fuenffachstern() ;

    for (i = [0:60:359])
    {
        rotate ([0, 0, 30+i]) translate ([35.5, 0, 0]) dreifachstern() ;
        rotate ([0, 0, 00+i]) translate ([41, 0, 0]) scale ([0.76, 0.76, 1]) doppelstern() ;
    }
}
```


[User]
I'd like to create a customizable 2D model of a French Bulldog, similar to the example I found on Thingiverse. The design should be based on a series of points and polygons that form the outline and inner cuts of the dog, with parameters to adjust the overall size, height, and thickness of the outline and inner lines.

Here are the specifics of what I'm looking for in the OpenSCAD files you'll generate:

1.  **Overall Structure:** The design should create a flat, 2D model of a French Bulldog using `linear_extrude` on a 2D shape.
2.  **Customizable Parameters:**
    *   `max_size_in_millimeter`:  A value that sets the maximum length of the longest side of the model. The other side should scale proportionately to maintain the original aspect ratio.
    *   `model_height_in_millimeter`:  The thickness of the 2D model, or its height after `linear_extrude`.
    *   `outline_size_in_millimeter`: The thickness of the outer outline, before the inner cuts.
    *   `inline_size_in_millimeter`: The thickness of the inner outline (cut lines).
    *   `inline_edge_radius_in_millimeter`: Controls the roundness of the edges of the inner lines.
    *   `flip_model`: A boolean or string parameter ("yes", "no") to flip the design along the y-axis.
3.  **Points and Polygons:** The model should be based on predefined 2D point coordinates (`A1`, `A2`, etc.) and then polygons built from these points (`C1`, `C2`, etc.). These are what define the shape of the dog and the internal cuts. The points and polygons should be defined directly in the code.
4.  **Scaling:**  The model should automatically calculate the necessary scaling factor to fit within the `max_size_in_millimeter` parameter while maintaining the original aspect ratio.
5.  **Outline and Inner Lines:** The outer outline should be created with an `offset` operation, and inner cutouts should be created by offsetting polygons using the `inline_size` parameter, also using an `offset` operation for the edges.
6.  **Flipping:** If `flip_model` is set to "yes", the entire model should be mirrored along the Y axis.
7.  **Module:** A module named `create()` should contain the logic for drawing the outlines and polygons with `linear_extrude`, `offset` and `polygon`, using the parameters defined.
8. **Preview:** The preview should be oriented to North and the tilt to Top.

I'm not expecting the AI to use any external libraries, so just standard OpenSCAD features should be enough.

[Assistant]
customizable_origami_french_bulldog.scad:
```
/*
 * Customizable Origami - French Bulldog - https://www.thingiverse.com/thing:2701954
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-12-09
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

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

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 2.2; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.8; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 1.7; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [1015, 153];
A2 = [1062, 212];
A3 = [1109, 153];
A4 = [1167, 275];
A5 = [1334, 335];
A6 = [1360, 485];
A7 = [1387, 519];
A8 = [1387, 565];
A9 = [1385, 645];
A10 = [1268, 686];
A11 = [1204, 919];
A12 = [1094, 1088];
A13 = [1055, 1195];
A14 = [1116, 1311];
A15 = [943, 1311];
A16 = [896, 1050];
A17 = [878, 961];
A18 = [566, 964];
A19 = [436, 1068];
A20 = [424, 1186];
A21 = [486, 1311];
A22 = [304, 1311];
A23 = [251, 1025];
A24 = [249, 793];
A25 = [290, 651];
A26 = [235, 587];
A27 = [230, 515];
A28 = [307, 490];
A29 = [357, 534];
A30 = [908, 534];
A31 = [986, 301];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31];

// Inner Points
B1 = [1101, 273];
B2 = [1240, 548];
B3 = [1321, 508];
B4 = [1025, 634];
B5 = [863, 726];
B6 = [367, 790];

// Polygons
C1 = [A31, A1, A2, B1];
C2 = [A2, A3, A4, B1];
C3 = [A31, B1, A4, A5, A6, B3, B2, A10, B4, A30];
C4 = [B2, A10, A9, A8, B3];
C5 = [B3, A6, A7, A8];
C6 = [A30, B4, A10, A11, A17, B5];
C7 = [A17, A11, A12, A16];
C8 = [A12, A13, A15, A16];
C9 = [A13, A14, A15];
C10 = [A17, B5, A30, A29, A25, B6, A18];
C11 = [A29, A28, A27, A26, A25];
C12 = [A25, B6, A18, A19, A23, A24];
C13 = [A19, A20, A22, A23];
C14 = [A20, A21, A22];

cut_polygons = [C1, C2, C3, C4, /*C5,*/ C6, C7, C8, C9, C10, C11, C12, C13, C14];

min_x = A27[0];
min_y = A1[1];
max_x = A7[0];
max_y = A21[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
I'd like to design a cookie cutter based on a modified hypotrochoid curve. I have an example SCAD file that gets me part of the way there, but I want to make it more parametric and flexible.

Here's the basic idea: The cookie cutter should have a main, generally circular body with a patterned edge, and a thin plate that extends out for easy handling. The main body should be a hollow shape formed by extruding a 2D curve. This curve should be based on the equations x(a,r,k) = (sin(a/k)+sin(a)/k)*r and y(a,r,k) = (cos(a/k)+cos(a)/k)*r, where 'r' is the radius, 'a' is the angle, and 'k' is a parameter that controls the number of arcs in the pattern. It will be useful to be able to control the wall thickness of the cutter, so the generated curve will need to be used as both the outer and inner edge with a scaling factor applied to the inner edge.

The cutter should have the following parameters:

*   `diameter`: The overall diameter of the cutter (this controls 'r').
*   `wall_thickness`: The thickness of the cutter walls.
*   `height`: The height of the main cutter body.
*   `num_arcs`: Controls the number of arcs/petals in the pattern. This maps to the 'k' parameter in the equations.
*  `plate_thickness`: The thickness of the plate.
*   `plate_offset`: The distance the plate extends outward from the main body.

The design should generate the main body by creating the outer and inner curves, extruding them, and then taking the difference between the two to create a hollow body. The plate should be added to the top of the hollow body and also be based on the same outer curve. This should also be a subtraction so that the plate only exists to the left of the main body.

Could you create an OpenSCAD file that does this? I don't need any external libraries; all the functions should be implemented in the file.

[Assistant]
keks.scad:
```

function x(a,r,k) = (sin(a/k)+sin(a)/k)*r;
function y(a,r,k) = (cos(a/k)+cos(a)/k)*r;

module f(r,k) {
    v1 = [ for (a = [0 : 1 : 180*k]) [ x(a,r,k), y(a,r,k)] ];
    v2 = [ for (a = [180*k : -1 : 0]) [ -x(a,r,k), y(a,r,k)] ];
    polygon(concat(v1,v2));
}

module keks(dia,d,h,k,p) {
    r = dia/2;
    sf=(r-d)/r;
    union() {
        linear_extrude(p)
        difference() {
            circle($fn=360,r+5);
            scale([sf,sf,h]) f(r,k+1);
        }
        linear_extrude(h)
        difference() {
            f(r,k+1);
            scale([sf,sf,h]) f(r,k+1);
        }
    }
}

/* Usage: keks(diameter,wall-thickness,height,segments,plate-thickness) */
keks(38,1,18,12,3);
```


[User]
I'd like to design a parametric Christmas wreath that I can either 3D print or laser cut. I've seen some nice examples, and I think I have a pretty good handle on what I want.

The basic idea is to create a wreath out of a number of identical, stylized "twig" pieces. These twig pieces will interlock at the base and tip, effectively connecting together to form the circle.

Here are the parameters I'd like to control:

*   **Wreath Radius:** This will determine the overall size of the wreath, and should be adjustable within a reasonable range, say 150mm to 500mm.

*   **Number of Twigs:**  This controls how many twig pieces make up the wreath. This value should be even, and adjustable within a range, say from 10 to 50, in steps of 2.

*   **Twig Thickness:** This parameter will control the thickness of the twig pieces. This is critical for 3D printing. I'd like a fine degree of adjustment here. Perhaps a range of 1 to 6 mm with 0.1mm increments?

*   **Show Assembled:**  I'd like a boolean option to render either a single twig or the full assembled wreath. Rendering the full wreath can be slow, especially with a high number of twigs, so being able to preview just one twig is important for quick adjustments.

Each twig should have a stylized branch-like shape, with the main branch and two smaller side branches branching from it, and then those secondary branches branching again to make a more filled out "twig." For the basic 2D twig shape, I think a combination of circles for the branch ends and rectangular segments would work.  The ends of each branch should be rounded by filleting the corners of the connecting rectangles.

For assembly, each twig will have a slot at the base and tip that allows it to connect to the adjacent twig. These slots should be at an angle that aligns the twigs to create the circular wreath.

I'd like the OpenSCAD files to include all the necessary modules and logic for these parameters and features. Can you help me design this? Also, I'm not sure if any standard libraries would help here. It's a fairly straightforward design, but I'm open to it if you think any would streamline things.

[Assistant]
xmaswreath.scad:
```
// Customizable Xmas Wreath
// by Stellar Innovation
// http://www.thingiverse.com/stellarinnovation/about
// licensed under the Creative Commons - Attribution license.

wreathRadius = 200;   // [150:500]
numTwigs = 24;        // [10:2:50]
thickness = 3.0;      // [1:0.1:6.0]
// displays entire wreath (warning: very slow!)
show_assembled = "no"; // [yes,no]

rotationStep = 360/numTwigs;
rollAngle = atan2(1, sqrt(cos(rotationStep)));
twigLength = 2*wreathRadius*sin(rotationStep)/sin((180-rotationStep)/2);
twigThickness = 3*thickness;

tol=0.1*1;

if (show_assembled == "yes")
  wreathPart();
else
  twigPart();

// One piece, of which numTwigs are needed to form a wreath
module twigPart() {
    rotate([0, -rollAngle])
    translate([wreathRadius, 0])
    difference() {
        twig3D(twigLength, rollAngle, 0);

        // diagonal slot at base of the twig
        rotate(rotationStep)
          translate([-wreathRadius, 0])
            rotate([0, -rollAngle])
              cube([twigLength, twigLength/2, thickness+2*tol], center=true);

        // slot at tip of twig
        rotate(-rotationStep)
          translate([-wreathRadius, 0])
            rotate([0, -rollAngle])
              cube([twigLength, twigLength/2, thickness+2*tol], center=true);
    }
}

// Complete wreath
module wreathPart() {
    for (i=[0:numTwigs-1]) {
      twig3D(twigLength,
              (2*floor(i/2) == i)? -rollAngle : rollAngle,
              i*rotationStep);
    }
}

// twig without slot with given length, roll (around its  own axis) and
// rotation around z-axis (rotZ)
module twig3D(length, roll, rotZ) {
   width = max(0.167*length, 3*thickness);

   rotate(rotZ)
     translate([-wreathRadius, 0])
       rotate([0, roll])
         translate([0, -length/2, -thickness/2])
           linear_extrude(height=thickness)
             level3Twig2D(length, width);
}

// 2D version of twig (all 9 branches)
module level3Twig2D(length, width) {
  l1 = 0.8*length;
  max_fillet = (l1-2.1*width)/0.73;

  level2Twig2D(length, width);
  rotate(72)
    level2Twig2D(l1, width);
  rotate(-72)
    level2Twig2D(l1, width);

  if (max_fillet > 0)
    upper_fillets(width, max_fillet);
}

// 2D twig with just three branches
module level2Twig2D(length, width) {
  h2 = 0.5*length;
  l2 = 0.5*length;
  max_upper_fillet = (length-l2-1.7*width)/0.73;
  max_lower_fillet = (length-l2-1.4*width)/0.73;

  basicTwig2D(length, width);

  translate([0, h2]) {
    rotate(72)
      basicTwig2D(l2, width);
    rotate(-72)
      basicTwig2D(l2, width);

    if (max_upper_fillet > 0)
      upper_fillets(width, max_upper_fillet);
    if (max_lower_fillet > 0)
      lower_fillets(width, max_lower_fillet);
  }
}

// 2D twig, a single branch
module basicTwig2D(length, width) {
    r = width/2;
    circle(d=width, $fn=24);
    translate([-r, 0])
      square([width, length-width]);
    translate([0, length-width])
      circle(d=width, $fn=24);
}

// "upper" fillets (the acute, 72 degree angles)
module upper_fillets(width, max_fillet) {
  r_fillet = min(5, 0.1*width, max_fillet);
  dx = 0.5*width;
  dy1 = dx*tan(18);
  dy2 = dx/cos(18);

  translate([dx, dy1+dy2])
    fillet2D(72, r_fillet);
  mirror([1, 0])
    translate([dx, dy1+dy2])
      fillet2D(72, r_fillet);
}

// "lower" fillets (the obtuse, 108 degree angles)
module lower_fillets(width, max_fillet) {
  r_fillet = min(5, 0.1*width, max_fillet);
  dx = 0.5*width;
  dy1 = dx*tan(18);
  dy2 = dx/cos(18);

  translate([dx, dy1-dy2])
    mirror([0, 1])
      fillet2D(108, r_fillet);
  mirror([1, 0])
    translate([dx, dy1-dy2])
      mirror([0, 1])
        fillet2D(108, r_fillet);
}

// fills in a fillet of an angle
module fillet2D(angle, radius) {
    dy = radius/tan(angle/2);
    x1 = dy*cos(90-angle);
    y1 = dy*sin(90-angle);

    difference() {
        polygon([[0, 0], [0,dy], [radius,dy], [x1,y1]]);

        translate([radius, dy])
          circle(r=radius, $fn=25);
    }
}

```


[User]
I'd like to create a customizable snowflake ornament with text on it. The snowflake should have a central design that repeats around a central point, and each branch should have two text blocks on it - one on each side of the branch.

Here's what I'm thinking:

*   **Main Text:** This text will be placed on each branch of the snowflake, and it will have a specified font, size, and vertical offset. I need to be able to change the text itself, its size, its vertical positioning, and the font/style.
*   **Additional Text:** This will also be on each branch, placed mirrored from the main text. It should also have its own font, size, and vertical offset. All of these parameters should be configurable.

The text on each branch should be mirrored and placed on both sides of each snowflake branch.

*   **Number of Branches:** I'd like to control how many branches the snowflake has, within a range of 1 to 12.
*   **Snowflake Thickness:** A simple height parameter for the overall thickness of the snowflake would be great.
*   **Font Options:** It should use fonts by name that are available in OpenSCAD. I'll need to be able to specify the font and its style (e.g. "Great Vibes:Italic" or "Roboto:Bold").

I've included the Google Fonts list from the previous file, which is extensive. I want to be able to select any font and style from that list.

Here are the parameters that should be customizable:
*   `main_text`: The main text string.
*   `main_text_size`: The size of the main text.
*   `main_text_vertical_offset`: The vertical offset of the main text.
*   `main_font`: The font name and style for the main text (e.g., "Great Vibes", "Roboto:Bold").
*    `additional_text`: The additional text string.
*   `additional_text_size`: The size of the additional text.
*   `additional_text_vertical_offset`: The vertical offset of the additional text.
*   `additional_font`: The font name and style for the additional text.
*   `number_of_branches`: The number of branches the snowflake has (1-12).
*   `height`: The overall thickness of the snowflake.

Could you generate the OpenSCAD code for this?

[Assistant]
Snowflake.scad:
```
// preview[view:south, tilt:top]
/* [Main Text] */
//To input the backslash type '\\'
main_text = "/";
main_text_size = 10;
main_text_vertical_offset = -1;
//See http://www.google.com/fonts. Handwriting fonts look the best for this thing.
main_font = "Great Vibes"; //[ABeeZee,Abel,Abril Fatface,Aclonica,Acme,Actor,Adamina,Advent Pro,Aguafina Script,Akronim,Aladin,Aldrich,Alef,Alegreya,Alegreya Sans,Alegreya Sans SC,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Alike,Alike Angular,Allan,Allerta,Allerta Stencil,Allura,Almendra,Almendra Display,Almendra SC,Amarante,Amaranth,Amatic SC,Amethysta,Amiri,Anaheim,Andada,Andada SC,Andika,Angkor,Annie Use Your Telescope,Anonymous Pro,Antic,Antic Didone,Antic Slab,Anton,Antonio,Arapey,Arbutus,Arbutus Slab,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arizonia,Armata,Artifika,Arvo,Asap,Asset,Astloch,Asul,Atomic Age,Aubrey,Audiowide,Autour One,Average,Average Sans,Averia Gruesa Libre,Averia Libre,Averia Sans Libre,Averia Serif Libre,Bad Script,Balthazar,Bangers,Basic,Battambang,Baumans,Bayon,Belgrano,Belleza,BenchNine,Bentham,Berkshire Swash,Bevan,Bhavuka,Bigelow Rules,Bigshot One,Bilbo,Bilbo Swash Caps,Bitter,Black Ops One,Bokor,Bonbon,Boogaloo,Bowlby One,Bowlby One SC,Brawler,Bree Serif,Bruno Ace,Bruno Ace SC,Bubblegum Sans,Bubbler One,Buda,Buenard,Butcherman,Butcherman Caps,Butterfly Kids,Cabin,Cabin Condensed,Cabin Sketch,Caesar Dressing,Cagliostro,Calligraffitti,Cambay,Cambo,Candal,Cantarell,Cantata One,Cantora One,Capriola,Cardo,Carme,Carrois Gothic,Carrois Gothic SC,Carter One,Caudex,Cedarville Cursive,Ceviche One,Changa,Changa One,Chango,Chau Philomene One,Chela One,Chelsea Market,Chenla,Cherry Cream Soda,Cherry Swash,Chewy,Chicle,Chivo,Cinzel,Cinzel Decorative,Clara,Clicker Script,Coda,Coda Caption,Codystar,Combo,Comfortaa,Coming Soon,Concert One,Condiment,Content,Contrail One,Convergence,Cookie,Copse,Corben,Courgette,Cousine,Coustard,Covered By Your Grace,Crafty Girls,Creepster,Creepster Caps,Crete Round,Crimson Text,Croissant One,Crushed,Cuprum,Cutive,Cutive Mono,Damion,Dancing Script,Dangrek,Dawning of a New Day,Days One,Dekko,Delius,Delius Swash Caps,Delius Unicase,Della Respira,Denk One,Devonshire,Dhurjati,Dhyana,Didact Gothic,Dinah,Diplomata,Diplomata SC,Domine,Donegal One,Doppio One,Dorsa,Dosis,Droid Arabic Kufi,Droid Arabic Naskh,Droid Sans,Droid Sans Ethiopic,Droid Sans Mono,Droid Sans Thai,Droid Serif,Droid Serif Thai,Dr Sugiyama,Duru Sans,Dynalight,Eagle Lake,Eater,Eater Caps,EB Garamond,Economica,Eczar,Ek Mukta,Electrolize,Elsie,Elsie Swash Caps,Emblema One,Emilys Candy,Encode Sans,Encode Sans Compressed,Encode Sans Condensed,Encode Sans Narrow,Encode Sans Wide,Engagement,Englebert,Enriqueta,Erica One,Esteban,Euphoria Script,Ewert,Exo,Exo 2,Expletus Sans,Fanwood Text,Fascinate,Fascinate Inline,Faster One,Fasthand,Fauna One,Federant,Federo,Felipa,Fenix,Finger Paint,Fira Mono,Fira Sans,Fjalla One,Fjord One,Flamenco,Flavors,Fondamento,Fontdiner Swanky,Forum,Francois One,Freckle Face,Fredericka the Great,Fredoka One,Freehand,Fresca,Frijole,Fruktur,Fugaz One,Gabriela,Gafata,Galdeano,Galindo,Gentium Basic,Gentium Book Basic,Geo,Geostar,Geostar Fill,Germania One,GFS Didot,GFS Neohellenic,Gidugu,Gilda Display,Give You Glory,Glass Antiqua,Glegoo,Gloria Hallelujah,Goblin One,Gochi Hand,Gorditas,Goudy Bookletter 1911,Graduate,Grand Hotel,Gravitas One,Great Vibes,Griffy,Gruppo,Gudea,Gurajada,Habibi,Halant,Hammersmith One,Hanalei,Hanalei Fill,Handlee,Hanuman,Happy Monkey,Headland One,Henny Penny,Hermeneus One,Herr Von Muellerhoff,Hind,Holtwood One SC,Homemade Apple,Homenaje,Iceberg,Iceland,IM Fell Double Pica,IM Fell Double Pica SC,IM Fell DW Pica,IM Fell DW Pica SC,IM Fell English,IM Fell English SC,IM Fell French Canon,IM Fell French Canon SC,IM Fell Great Primer,IM Fell Great Primer SC,Imprima,Inconsolata,Inder,Indie Flower,Inika,Inknut Antiqua,Irish Grover,Irish Growler,Istok Web,Italiana,Italianno,Jacques Francois,Jacques Francois Shadow,Jaldi,Jim Nightshade,Jockey One,Jolly Lodger,Josefin Sans,Josefin Sans Std Light,Josefin Slab,Joti One,Judson,Julee,Julius Sans One,Junge,Jura,Just Another Hand,Just Me Again Down Here,Kadwa,Kalam,Kameron,Kantumruy,Karla,Karla Tamil Inclined,Karla Tamil Upright,Karma,Kaushan Script,Kavoon,Kdam Thmor,Keania One,Kelly Slab,Kenia,Khand,Khmer,Khula,Kite One,Knewave,Kotta One,Koulen,Kranky,Kreon,Kristi,Krona One,La Belle Aurore,Laila,Lakki Reddy,Lancelot,Lateef,Lato,League Script,Leckerli One,Ledger,Lekton,Lemon,Lemon One,Liberation Sans ,Libre Baskerville,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Linden Hill,Lobster,Lobster Two,Lohit Bengali,Lohit Tamil,Londrina Outline,Londrina Shadow,Londrina Sketch,Londrina Solid,Lora,Loved by the King,Lovers Quarrel,Love Ya Like A Sister,Luckiest Guy,Lusitana,Lustria,Macondo,Macondo Swash Caps,Magra,Maiden Orange,Mako,Mallanna,Mandali,Marcellus,Marcellus SC,Marck Script,Margarine,Marko One,Marmelad,Martel Sans,Marvel,Mate,Mate SC,Maven Pro,McLaren,Meddon,MedievalSharp,Medula One,Megrim,Meie Script,Merge One,Merienda,Merienda One,Merriweather,Merriweather Sans,Mervale Script,Metal,Metal Mania,Metamorphous,Metrophobic,Miama,Michroma,Milonga,Miltonian,Miltonian Tattoo,Miniver,Miss Fajardose,Miss Saint Delafield,Modak,Modern Antiqua,Molengo,Molle,Monda,Monofett,Monoton,Monsieur La Doulaise,Montaga,Montez,Montserrat,Montserrat Alternates,Montserrat Subrayada,Moul,Moulpali,Mountains of Christmas,Mouse Memoirs,Mr Bedford,Mr Bedfort,Mr Dafoe,Mr De Haviland,Mrs Sheppards,Mrs Saint Delafield,Muli,Mystery Quest,Nanum Brush Script,Nanum Gothic,Nanum Gothic Coding,Nanum Myeongjo,Nanum Pen Script,NATS,Neucha,Neuton,New Rocker,News Cycle,Niconne,Nixie One,Nobile,Nokora,Norican,Nosifer,Nosifer Caps,Nothing You Could Do,Noticia Text,Noto Sans,Noto Sans Hebrew,Noto Sans Symbols,Noto Sans UI,Noto Serif,Nova Cut,Nova Flat,Nova Mono,Nova Oval,Nova Round,Nova Script,Nova Slim,Nova Square,NTR,Numans,Nunito,Odor Mean Chey,Offside,OFL Sorts Mill Goudy TT,Oldenburg,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Open Sans,Open Sans Condensed,Oranienbaum,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Overlock,Overlock SC,Over the Rainbow,Ovo,Oxygen,Oxygen Mono,Pacifico,Palanquin,Palanquin Dark,Paprika,Parisienne,Passero One,Passion One,Pathway Gothic One,Patrick Hand,Patrick Hand SC,Patua One,Paytone One,Pecita,Peddana,Peralta,Permanent Marker,Petit Formal Script,Petrona,Phetsarath,Philosopher,Piedra,Pinyon Script,Pirata One,Plaster,Play,Playball,Playfair Display,Playfair Display SC,Podkova,Poetsen One,Poiret One,Poller One,Poly,Pompiere,Ponnala,Pontano Sans,Poppins,Porter Sans Block,Port Lligat Sans,Port Lligat Slab,Prata,Preahvihear,Press Start 2P,Princess Sofia,Prociono,Prosto One,PT Mono,PT Sans,PT Sans Caption,PT Sans Narrow,PT Serif,PT Serif Caption,Puralecka Narrow,Puritan,Purple Purse,Quando,Quantico,Quattrocento,Quattrocento Sans,Questrial,Quicksand,Quintessential,Qwigley,Racing Sans One,Radley,Rajdhani,Raleway,Raleway Dots,Ramabhadra,Ramaraja,Rambla,Rammetto One,Ranchers,Rancho,Ranga,Rationale,Ravi Prakash,Redacted,Redacted Script,Redressed,Reenie Beanie,Revalia,Ribeye,Ribeye Marrow,Righteous,Risque,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rokkitt,Romanesco,Ropa Sans,Rosario,Rosarivo,Rouge Script,Rozha One,Rubik Mono One,Rubik One,Ruda,Rufina,Ruge Boogie,Ruluko,Rum Raisin,Ruslan Display,Russo One,Ruthie,Rye,Sacramento,Sail,Salsa,Sanchez,Sancreek,Sansation,Sansita One,Sarabun,Sarala,Sarina,Sarpanch,Satisfy,Scada,Scheherazade,Schoolbell,Seaweed Script,Sedan,Sedan SC,Sevillana,Seymour One,Shadows Into Light,Shadows Into Light Two,Shanti,Share,Share Tech,Share Tech Mono,Shojumaru,Short Stack,Siamreap,Siemreap,Sigmar One,Signika,Signika Negative,Simonetta,Sintony,Sirin Stencil,Six Caps,Skranji,Slabo 13px,Slabo 27px,Slackey,Smokum,Smythe,Sniglet,Snippet,Snowburst One,Sofadi One,Sofia,Sonsie One,Sorts Mill Goudy,Source Code Pro,Source Sans Pro,Source Serif Pro,Special Elite,Spicy Rice,Spinnaker,Spirax,Squada One,Sree Krushnadevaraya,Stalemate,Stalinist One,Stalin One,Stardos Stencil,Stint Ultra Condensed,Stint Ultra Expanded,Stoke,Strait,Strong,Sue Ellen Francisco,Sunshiney,Supermercado One,Suranna,Suravaram,Suwannaphum,Swanky and Moo Moo,Syncopate,Tangerine,Taprom,Tauri,Teko,Telex,Tenali Ramakrishna,Tenor Sans,Terminal Dosis,Terminal Dosis Light,Text Me One,Thabit,The Girl Next Door,Tienne,Tillana,Timmana,Tinos,Titan One,Titillium Web,Trade Winds,Trocchi,Trochut,Trykker,Tuffy,Tulpen One,Ubuntu,Ubuntu Condensed,Ubuntu Mono,Ultra,Uncial Antiqua,Underdog,Unica One,UnifrakturCook,UnifrakturMaguntia,Unkempt,Unlock,Unna,Vampiro One,Varela,Varela Round,Vast Shadow,Vesper Libre,Vibur,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,VT323,Waiting for the Sunrise,Wallpoet,Walter Turncoat,Warnes,Wellfleet,Wendy One,Wire One,Yanone Kaffeesatz,Yellowtail,Yeseva One,Yesteryear,Zeyada]
main_text_style =  "";//[:Normal,style=Italic:Italic,style=Bold:Bold]
/* [Additional Text] */
//To input the backslash type '\\'
additional_text = "MT";
additional_text_size = 5;
additional_text_vertical_offset = -2;
//See http://www.google.com/fonts. Handwriting fonts look the best for this thing.
additional_font = "Great Vibes"; //[ABeeZee,Abel,Abril Fatface,Aclonica,Acme,Actor,Adamina,Advent Pro,Aguafina Script,Akronim,Aladin,Aldrich,Alef,Alegreya,Alegreya Sans,Alegreya Sans SC,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Alike,Alike Angular,Allan,Allerta,Allerta Stencil,Allura,Almendra,Almendra Display,Almendra SC,Amarante,Amaranth,Amatic SC,Amethysta,Amiri,Anaheim,Andada,Andada SC,Andika,Angkor,Annie Use Your Telescope,Anonymous Pro,Antic,Antic Didone,Antic Slab,Anton,Antonio,Arapey,Arbutus,Arbutus Slab,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arizonia,Armata,Artifika,Arvo,Asap,Asset,Astloch,Asul,Atomic Age,Aubrey,Audiowide,Autour One,Average,Average Sans,Averia Gruesa Libre,Averia Libre,Averia Sans Libre,Averia Serif Libre,Bad Script,Balthazar,Bangers,Basic,Battambang,Baumans,Bayon,Belgrano,Belleza,BenchNine,Bentham,Berkshire Swash,Bevan,Bhavuka,Bigelow Rules,Bigshot One,Bilbo,Bilbo Swash Caps,Bitter,Black Ops One,Bokor,Bonbon,Boogaloo,Bowlby One,Bowlby One SC,Brawler,Bree Serif,Bruno Ace,Bruno Ace SC,Bubblegum Sans,Bubbler One,Buda,Buenard,Butcherman,Butcherman Caps,Butterfly Kids,Cabin,Cabin Condensed,Cabin Sketch,Caesar Dressing,Cagliostro,Calligraffitti,Cambay,Cambo,Candal,Cantarell,Cantata One,Cantora One,Capriola,Cardo,Carme,Carrois Gothic,Carrois Gothic SC,Carter One,Caudex,Cedarville Cursive,Ceviche One,Changa,Changa One,Chango,Chau Philomene One,Chela One,Chelsea Market,Chenla,Cherry Cream Soda,Cherry Swash,Chewy,Chicle,Chivo,Cinzel,Cinzel Decorative,Clara,Clicker Script,Coda,Coda Caption,Codystar,Combo,Comfortaa,Coming Soon,Concert One,Condiment,Content,Contrail One,Convergence,Cookie,Copse,Corben,Courgette,Cousine,Coustard,Covered By Your Grace,Crafty Girls,Creepster,Creepster Caps,Crete Round,Crimson Text,Croissant One,Crushed,Cuprum,Cutive,Cutive Mono,Damion,Dancing Script,Dangrek,Dawning of a New Day,Days One,Dekko,Delius,Delius Swash Caps,Delius Unicase,Della Respira,Denk One,Devonshire,Dhurjati,Dhyana,Didact Gothic,Dinah,Diplomata,Diplomata SC,Domine,Donegal One,Doppio One,Dorsa,Dosis,Droid Arabic Kufi,Droid Arabic Naskh,Droid Sans,Droid Sans Ethiopic,Droid Sans Mono,Droid Sans Thai,Droid Serif,Droid Serif Thai,Dr Sugiyama,Duru Sans,Dynalight,Eagle Lake,Eater,Eater Caps,EB Garamond,Economica,Eczar,Ek Mukta,Electrolize,Elsie,Elsie Swash Caps,Emblema One,Emilys Candy,Encode Sans,Encode Sans Compressed,Encode Sans Condensed,Encode Sans Narrow,Encode Sans Wide,Engagement,Englebert,Enriqueta,Erica One,Esteban,Euphoria Script,Ewert,Exo,Exo 2,Expletus Sans,Fanwood Text,Fascinate,Fascinate Inline,Faster One,Fasthand,Fauna One,Federant,Federo,Felipa,Fenix,Finger Paint,Fira Mono,Fira Sans,Fjalla One,Fjord One,Flamenco,Flavors,Fondamento,Fontdiner Swanky,Forum,Francois One,Freckle Face,Fredericka the Great,Fredoka One,Freehand,Fresca,Frijole,Fruktur,Fugaz One,Gabriela,Gafata,Galdeano,Galindo,Gentium Basic,Gentium Book Basic,Geo,Geostar,Geostar Fill,Germania One,GFS Didot,GFS Neohellenic,Gidugu,Gilda Display,Give You Glory,Glass Antiqua,Glegoo,Gloria Hallelujah,Goblin One,Gochi Hand,Gorditas,Goudy Bookletter 1911,Graduate,Grand Hotel,Gravitas One,Great Vibes,Griffy,Gruppo,Gudea,Gurajada,Habibi,Halant,Hammersmith One,Hanalei,Hanalei Fill,Handlee,Hanuman,Happy Monkey,Headland One,Henny Penny,Hermeneus One,Herr Von Muellerhoff,Hind,Holtwood One SC,Homemade Apple,Homenaje,Iceberg,Iceland,IM Fell Double Pica,IM Fell Double Pica SC,IM Fell DW Pica,IM Fell DW Pica SC,IM Fell English,IM Fell English SC,IM Fell French Canon,IM Fell French Canon SC,IM Fell Great Primer,IM Fell Great Primer SC,Imprima,Inconsolata,Inder,Indie Flower,Inika,Inknut Antiqua,Irish Grover,Irish Growler,Istok Web,Italiana,Italianno,Jacques Francois,Jacques Francois Shadow,Jaldi,Jim Nightshade,Jockey One,Jolly Lodger,Josefin Sans,Josefin Sans Std Light,Josefin Slab,Joti One,Judson,Julee,Julius Sans One,Junge,Jura,Just Another Hand,Just Me Again Down Here,Kadwa,Kalam,Kameron,Kantumruy,Karla,Karla Tamil Inclined,Karla Tamil Upright,Karma,Kaushan Script,Kavoon,Kdam Thmor,Keania One,Kelly Slab,Kenia,Khand,Khmer,Khula,Kite One,Knewave,Kotta One,Koulen,Kranky,Kreon,Kristi,Krona One,La Belle Aurore,Laila,Lakki Reddy,Lancelot,Lateef,Lato,League Script,Leckerli One,Ledger,Lekton,Lemon,Lemon One,Liberation Sans ,Libre Baskerville,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Linden Hill,Lobster,Lobster Two,Lohit Bengali,Lohit Tamil,Londrina Outline,Londrina Shadow,Londrina Sketch,Londrina Solid,Lora,Loved by the King,Lovers Quarrel,Love Ya Like A Sister,Luckiest Guy,Lusitana,Lustria,Macondo,Macondo Swash Caps,Magra,Maiden Orange,Mako,Mallanna,Mandali,Marcellus,Marcellus SC,Marck Script,Margarine,Marko One,Marmelad,Martel Sans,Marvel,Mate,Mate SC,Maven Pro,McLaren,Meddon,MedievalSharp,Medula One,Megrim,Meie Script,Merge One,Merienda,Merienda One,Merriweather,Merriweather Sans,Mervale Script,Metal,Metal Mania,Metamorphous,Metrophobic,Miama,Michroma,Milonga,Miltonian,Miltonian Tattoo,Miniver,Miss Fajardose,Miss Saint Delafield,Modak,Modern Antiqua,Molengo,Molle,Monda,Monofett,Monoton,Monsieur La Doulaise,Montaga,Montez,Montserrat,Montserrat Alternates,Montserrat Subrayada,Moul,Moulpali,Mountains of Christmas,Mouse Memoirs,Mr Bedford,Mr Bedfort,Mr Dafoe,Mr De Haviland,Mrs Sheppards,Mrs Saint Delafield,Muli,Mystery Quest,Nanum Brush Script,Nanum Gothic,Nanum Gothic Coding,Nanum Myeongjo,Nanum Pen Script,NATS,Neucha,Neuton,New Rocker,News Cycle,Niconne,Nixie One,Nobile,Nokora,Norican,Nosifer,Nosifer Caps,Nothing You Could Do,Noticia Text,Noto Sans,Noto Sans Hebrew,Noto Sans Symbols,Noto Sans UI,Noto Serif,Nova Cut,Nova Flat,Nova Mono,Nova Oval,Nova Round,Nova Script,Nova Slim,Nova Square,NTR,Numans,Nunito,Odor Mean Chey,Offside,OFL Sorts Mill Goudy TT,Oldenburg,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Open Sans,Open Sans Condensed,Oranienbaum,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Overlock,Overlock SC,Over the Rainbow,Ovo,Oxygen,Oxygen Mono,Pacifico,Palanquin,Palanquin Dark,Paprika,Parisienne,Passero One,Passion One,Pathway Gothic One,Patrick Hand,Patrick Hand SC,Patua One,Paytone One,Pecita,Peddana,Peralta,Permanent Marker,Petit Formal Script,Petrona,Phetsarath,Philosopher,Piedra,Pinyon Script,Pirata One,Plaster,Play,Playball,Playfair Display,Playfair Display SC,Podkova,Poetsen One,Poiret One,Poller One,Poly,Pompiere,Ponnala,Pontano Sans,Poppins,Porter Sans Block,Port Lligat Sans,Port Lligat Slab,Prata,Preahvihear,Press Start 2P,Princess Sofia,Prociono,Prosto One,PT Mono,PT Sans,PT Sans Caption,PT Sans Narrow,PT Serif,PT Serif Caption,Puralecka Narrow,Puritan,Purple Purse,Quando,Quantico,Quattrocento,Quattrocento Sans,Questrial,Quicksand,Quintessential,Qwigley,Racing Sans One,Radley,Rajdhani,Raleway,Raleway Dots,Ramabhadra,Ramaraja,Rambla,Rammetto One,Ranchers,Rancho,Ranga,Rationale,Ravi Prakash,Redacted,Redacted Script,Redressed,Reenie Beanie,Revalia,Ribeye,Ribeye Marrow,Righteous,Risque,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rokkitt,Romanesco,Ropa Sans,Rosario,Rosarivo,Rouge Script,Rozha One,Rubik Mono One,Rubik One,Ruda,Rufina,Ruge Boogie,Ruluko,Rum Raisin,Ruslan Display,Russo One,Ruthie,Rye,Sacramento,Sail,Salsa,Sanchez,Sancreek,Sansation,Sansita One,Sarabun,Sarala,Sarina,Sarpanch,Satisfy,Scada,Scheherazade,Schoolbell,Seaweed Script,Sedan,Sedan SC,Sevillana,Seymour One,Shadows Into Light,Shadows Into Light Two,Shanti,Share,Share Tech,Share Tech Mono,Shojumaru,Short Stack,Siamreap,Siemreap,Sigmar One,Signika,Signika Negative,Simonetta,Sintony,Sirin Stencil,Six Caps,Skranji,Slabo 13px,Slabo 27px,Slackey,Smokum,Smythe,Sniglet,Snippet,Snowburst One,Sofadi One,Sofia,Sonsie One,Sorts Mill Goudy,Source Code Pro,Source Sans Pro,Source Serif Pro,Special Elite,Spicy Rice,Spinnaker,Spirax,Squada One,Sree Krushnadevaraya,Stalemate,Stalinist One,Stalin One,Stardos Stencil,Stint Ultra Condensed,Stint Ultra Expanded,Stoke,Strait,Strong,Sue Ellen Francisco,Sunshiney,Supermercado One,Suranna,Suravaram,Suwannaphum,Swanky and Moo Moo,Syncopate,Tangerine,Taprom,Tauri,Teko,Telex,Tenali Ramakrishna,Tenor Sans,Terminal Dosis,Terminal Dosis Light,Text Me One,Thabit,The Girl Next Door,Tienne,Tillana,Timmana,Tinos,Titan One,Titillium Web,Trade Winds,Trocchi,Trochut,Trykker,Tuffy,Tulpen One,Ubuntu,Ubuntu Condensed,Ubuntu Mono,Ultra,Uncial Antiqua,Underdog,Unica One,UnifrakturCook,UnifrakturMaguntia,Unkempt,Unlock,Unna,Vampiro One,Varela,Varela Round,Vast Shadow,Vesper Libre,Vibur,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,VT323,Waiting for the Sunrise,Wallpoet,Walter Turncoat,Warnes,Wellfleet,Wendy One,Wire One,Yanone Kaffeesatz,Yellowtail,Yeseva One,Yesteryear,Zeyada]
additional_text_style =  "";//[:Normal,style=Italic:Italic,style=Bold:Bold]
/* [Other] */
number_of_branches = 6; //[1:12]
//Snowflake's thickness
height = 1;

Snowflake(main_text, additional_text, main_text_size, additional_text_size, height, main_text_vertical_offset, additional_text_vertical_offset, str(main_font,":",main_text_style), str(additional_font,":",additional_text_style),number_of_branches);

// Module instantiation
module Snowflake(mText = "", aText = "", mSize = 10, aSize = 5, height = 1, mOffset = 0, aOffset=0, mFont, aFont, number=6)
union()
for(i = [1 : 1 : number]){
    rotate([0,0,(360/number)*(i-1)]){
        LetterBlock(mText, mSize, height, mFont, mOffset);
    }
    mirror([0,1,0])
        rotate([0,0,(360/number/2)+(360/number)*(i-1)])
            LetterBlock(aText, aSize, height, aFont, aOffset);
};

// Module definition.
// size=30 defines an optional parameter with a default value.
module LetterBlock(letter, size, height, font, offset) {
    union(){
        translate([0,offset,0])
            linear_extrude(height=height)
                text(letter,
                     size=size,
                     font=font,
                     halign="left",
                     valign="bottom");
        translate([0,-offset,0])
        mirror([0,1,0])
            linear_extrude(height=height)
                text(letter,
                     size=size,
                     font=font,
                     halign="left",
                     valign="bottom");
    }
}

// Written by Jexom <jexomwtf@gmail.com>

```


[User]
Hi, I'm looking to create some interesting 3D printable shapes based on the supershape formula, and I'd like to have a parametric OpenSCAD design I can customize. I've seen some examples online, and I'm particularly interested in the flexibility of the shapes they produce.

Specifically, I need the following features:

1.  **Shape Selection:** I need to be able to choose between a full supershape and a half supershape (like a dome).
2.  **Two Supershape Sets of Parameters:** The design needs two sets of supershape parameters: one for the longitudinal direction and one for the latitudinal direction. Each set should have `m`, `n1`, `n2`, and `n3` parameters.
3.  **Z-Scale:**  I need to be able to control the scale of the shape in the Z direction, independently of the X and Y scale.
4.  **Twist:** I'd like a "twist" parameter that will rotate the longitude profile based on the latitude.
5.  **Waving:**  It would be really cool if I could add a "waving" effect to the shape, controlled by a height and radius parameter. This would modify the radius of the object based on cosine functions.
6.  **Fine Grain Control:** I need to be able to define the number of meridians and parallels via parameters, along with the calculation `stepP` and `stepM` for calculation. It would also be useful to be able to change the radii of the supershape calculation (the a and b values) even if they are set to 1 by default.
7.  **Presets:** Include some commented out example parameter sets for inspiration, such as the "cactus" or "heart" examples referenced above.

The output should be a single OpenSCAD file I can open and render. I'm not requesting that you use libraries like MCAD, BOSL, or BOLTS. I don't need the hook STLs referenced above, just the shapes based on supershape formula.

[Assistant]
supershapes.scad:
```
/*

    SuperShapes generator
    Fernando Jerez 11/12/2017

    License: CC-Attribution

*/

// Full shape or Half-top
shape = "half"; // [full: Full Shape, half:Half-top]

// Shape1
s1_m = 6;
s1_n1 = 0.5;
s1_n2 = 1.7;
s1_n3 = 1;

// Shape2
s2_m = 10;
s2_n1 = 0.15;
s2_n2 = 1.7;
s2_n3 = 3;

// Z-Scale
zscale=1;

// Twist multiplier (0 = no twist)
twist = 0; // [-2:0.1:2]
// Waving height
waving = 0; // [0:10]
// Waving radius
waving_r = 0; //[0:10]

/* [Hidden] */
r = 50;
stepP = 1;
stepM = 2;
meridians = 360/stepM;

inilat = (shape=="half")?0:-90;
endlat = 90;
parallels = (endlat-inilat) / stepP;
a=1;
b=1;

sshape1 = [s1_m,s1_n1,s1_n2,s1_n3]; //longitude
sshape2 = [s2_m,s2_n1,s2_n2,s2_n3]; //latitude

// samples
/* SHAPE1
sshape1 = [7,0.20,1.70,1.70]; //longitude
sshape2 = [7,0.20,1.70,1.70]; //latitude
*/
/* SHAPE 2
sshape1 = [6,0.50,1,2.50]; //longitude
sshape2 = [20,10,5,5]; //latitude
*/
/* SHAPE 3
sshape1 = [8,60,55,1000]; //longitude
sshape2 = [4,250,1000,100]; //latitude
*/
/* Shape 4
sshape1 = [5,0.10,1.70,1.70]; //longitude
sshape2 = [1,0.30,0.50,0.50]; //latitude
*/

/* Cactus
sshape1 = [20,2,0,15]; //longitude
sshape2 = [7,1,0,20]; //latitude
*/

/* heart
sshape1 = [2,0.10,2.50,1.70]; //longitude
sshape2 = [40,8,40,0]; //latitude
*/

// Superformula
function superf(ang,n)=
    let(
     t1 = pow( abs((1/a) * cos(n[0]*ang/4)   ),n[2]),
     t2 = pow( abs((1/b) * sin(n[0]*ang/4)   ),n[3]),
     r = pow(t1+t2,-1/n[1])
    ) r;

// Build Polyhedron
scale([1,1,1])
rotate([0,0,0])
polyhedron(
points =
[
    for(lat=[inilat:stepP:endlat-1])
        for(lon=[-180:stepM:180-stepM])
            let
            (
            r1 = superf(lon+lat*twist,sshape1),
            r2 = superf(lat,sshape2),
            r = 40 +10*cos(lon*waving_r),
            x = r* (r1*cos(lon)*r2*cos(lat)),
            y = r* (r1*sin(lon)*r2*cos(lat)) ,
            z = (r*zscale) * r2*sin(lat) * (0.6+0.4*cos((lon)*waving))
            )
            [x,y,z]

]
,
faces =
concat(
    [
    for(z = [0:parallels-2])
        for(s = [0:meridians-1])
            let(
                // clockwise from left-down corner
                f1 = s + meridians*z,
                f2 = s + meridians*(z+1),
                f3 = ((s+1) % meridians) + meridians*(z+1),
                f4 = ((s+1) % meridians) + meridians*z
            )

            [f1,f2,f3,f4]

    ],
    [[ for(s = [0:meridians-1]) ((inilat==0)?0:meridians)+s]], // bottom
    [[ for(s = [1:meridians]) meridians*(parallels)-s]] // top

)

);
```


[User]
Okay, I'm looking to get a custom phone case designed for my Nexus 6, and I really like the functionality of the one I saw on Thingiverse. It's a wallet-style case, but I'd like it to be completely parametric so I can tweak it as needed. The original design uses a 3D model of the phone, which I'd like to have included somehow, or be able to swap out for another model if needed.

Specifically, I need the design to include:

*   **A Bumper:** This forms the main part of the case and should wrap securely around the phone. I need to be able to control the thickness of the sides, top, and bottom of this bumper.
*   **A Top/Lid:** This part covers the screen and can have different configurations. I want the option to have no top (for a simple bumper), or a full top. The thickness of the top should be adjustable.
*   **Hinge:** If a top is present, there needs to be a hinge connecting it to the bumper. The size and thickness of this hinge should be parameterized. There should also be extra space between the top and the phone, that should be customizable.
*   **Credit Card Pockets:** I need to be able to add multiple credit card pockets between the top/lid and the phone. The number of pockets and the spacing between them needs to be adjustable. I also need to control the height and top thickness of the credit card pockets.
*   **Air Gaps:** The credit card pockets will utilize air gaps, which is great! The number of layers used to make these gaps should be parameterized.
*   **An Extra Pocket:** I'd like to add an extra, full-length pocket, similar to the credit card pockets, but that is not segmented into individual card slots. This should be configurable and placed between the top and the phone.
*   **A Stand:** I would like the option to add a stand that can be configured to be a specific distance from the bottom of the case. This will also utilize the air gap tech.
*   **Cutouts:** I need to incorporate cutouts for the headphone jack, power button, USB port, and camera. The positions and sizes of these cutouts should be adjustable.
*   **Rounding:** The case should have rounded corners and edges to make it more comfortable to hold. These should be configurable parameters.
*   **Engraved Text:** I'd also love the ability to add some engraved text, controlling font, size, spacing, position and depth.
*   **Material:** I'm planning on using flexible materials like Semiflex, Cheetah, or Ninjaflex, so the design should be able to account for that. This seems to be done in the existing design by using a variable for extra space that can be set to negative values.

I'd prefer if the entire design was based on modules that can be used and combined, and I think it makes sense for the phone model itself to be a separate module. I also need to account for print precision issues using custom variables.

I'm hoping for a clean, organized design that's easy to adjust. Can you generate a parametric OpenSCAD file or files that meet these specifications? Please use standard OpenSCAD libraries.

[Assistant]
nexus6_wallet_case.scad:
```
// Author: Christian Petry
// Designed in Switzerland 2017
// Version: 1.2 (2018-02-11)

// uses model of nexus 6 by Samg381 https://www.thingiverse.com/thing:755283
// Thank you!

/* Version History
V1.0 (2017-12-12) initial version
- based on my oneplus3 wallet case

V1.1 (2018-01-11)
- phoneDimensionAdditional X/Y/Z -0.25 -> -0.5
- phoneZCorrection 0 -> 2
- creditCardSpace[0] increased by 1.2mm
- caseUndersideThickness 1.8 -> 1.2
- hingeAdditional 2.75 -> 5.0

V1.2 (2018-02-11)
- fixed: model of nexus 6 is not correct, had to add corrections parts
-- phoneZCorrection 2 -> 4.8
-- phoneDimensionAdditional X/Y -0.25 -> 1.0
-- caseUndersideThickness 1.2 -> -1.4
-- hingeAdditional 3.75 -> 6.2
- standLayer 6 -> 5

*/

/* [Main] */
layerHeight=0.15;
caseSideThickness=1.8;
caseTopBottomThickness=1.8;
caseUndersideThickness=-1.4;
// position of the stand layer; -1=none
standLayer=5; // [-1:1:20]
// position of the extra pocket layer; 0=none
extraTopPocketLayer=6; // [0:1:20]
// number of credit card pockets; 0=none
numberCreditCardPockets=3; // [0:1:4]
// height of top without credit card pockets; 0=no top, i.e. bumper case
topZLayers=10; // [0:1:20]
// credit card pocket top thickness
creditCardPocketTopLayers=3; // [1:1:5]
// number of layers for air gap
airGapLayers=2; // [1:1:3]
// hinge thickness; 0=none
hingeZLayers=2; // [0:1:20]
// extra space for credit cards and money between phone and top
hingeAdditional=6.2;
// make additional space for phone, use i.e. to offset print precision issues. For very flexible materials you may want to use negative values
phoneDimensionAdditionalX=1.0;
phoneDimensionAdditionalY=1.0;
phoneDimensionAdditionalZ=0;

// engraved text
engravedText="Doris";
engravedTextLayers=2;
engravedTextSize=9;
engravedTextSpacing=1.1;
engravedTextPosX=-103;
engravedTextPosY=-70;
engravedTextFont="Liberation Sans";

/* [Hidden] */
$fn=50;
phoneZCorrection=4.8; // workaround for errors in modeled phone
phone=[82.98+phoneDimensionAdditionalX,159.26+phoneDimensionAdditionalY,10.06+phoneDimensionAdditionalZ]; // phone dimesions x,y,z
path="Nexus6.stl";

// --- cutouts ---
// top (headphone)
topCutout=[8-2,19,30];
posCutout1=[0,-3,4];
// power button
powerbutton=[42-8,24,30];
posCutout2=[9,-3,3.5];
// bottom (usb)
bottomCutout=[10-4,16,30];
posCutout3=[0,-1,6.0];
// camera
camera_loc=[0,57];
camera=[20,20,20];
camera_rounding=camera[0]/2-0.01;

// --- cutoffs ---
case_top_cutoff=-99;
case_bottom_cutoff=3;

// --- phone and case spefific rounding ---
phone_edge=11;
phone_edge_r=37;
rounding=1.7;
s_scale=[3.5,3.5,1];
case_rounding=5;
case_rounding_sphere=2.5;
smallerX=18;
smallerY=15;

// case size offset x,y,z
caseWall=[caseSideThickness,
caseTopBottomThickness, caseUndersideThickness+case_bottom_cutoff];

hingeX=phone[2]+caseWall[0]+caseWall[2]-case_bottom_cutoff+hingeAdditional;
hingeWidth=hingeX+hingeAdditional;
hingeZ=hingeZLayers*layerHeight;

topZ=topZLayers*layerHeight;

OVERLAP=0.005;

function creditCardZ()=airGapLayers*layerHeight;

creditCardSpace=[65.4+1.2,75]; // x in mm, y in mm
bottomThickness=0;
creditCardPocketSeam=1;
seamTopPocket=1.5;
creditCardPocketTop=layerHeight*creditCardPocketTopLayers;
zOffset=0+creditCardZ()+creditCardPocketTop;
creditCardPocketsStartOffset=2;
creditCardPocketYOffsetPerPocket=19;
creditCardPocketHeight=bottomThickness+creditCardZ()+creditCardPocketTop;
extraYOffsetPerPocket=2.5;

difference(){
    bumper();
    standCutout();
}

if (topZLayers > 0){
    hinge();
    difference(){
        top();
        extraTopPocket();
        translate([0,0,-OVERLAP])
        engravedText();
    }

    if (numberCreditCardPockets > 0){
        translate([0,creditCardPocketsStartOffset,0])
        creditCardPockets();
    }
}

module bumper(){
    translate([0,0,-case_bottom_cutoff])
    difference(){
        difference(){
            hull(){
                resize(newsize=[phone[0]+2*caseWall[0],phone[1]+2*caseWall[1],phone[2]+caseWall[2]+phoneZCorrection])
                phone();

                translate([-phone[0]/2+smallerX/2,-phone[1]/2+smallerY/2,0])
                minkowski(){
                    cube([phone[0]-smallerX,phone[1]-smallerY,6]);
                    cylinder(r=case_rounding*1.5,h=case_rounding);
                }
            }

            translate([0,0,phone[2]+caseWall[2]+10/2-case_top_cutoff])
            cube([phone[0]*2,phone[1]*2,10], center=true);

            translate([0,0,-30/2+case_bottom_cutoff])
            cube([phone[0]*2,phone[1]*2,30], center=true);
        }
        translate([0,0,caseWall[2]])
        union(){
            resize(newsize=[phone[0],phone[1],phone[2]+phoneZCorrection])
            phone();
            phone_cutouts();
        }
    }

    difference(){
        union(){
            translate([-34.5,-phone[1]/2-0.67,0])
            cube([67,phone[1]+0.2,1.3]);

            translate([-28,-81.2,0.2])
            cube([54,8,4.6]);

            translate([-35,-80.2,0.2])
            cube([70,8,4.6]);

            translate([-33,-72.3,0.2])
            cube([65,15,3.4]);

            translate([-33,-57.4,0.2])
            cube([65,15,2.5]);

            translate([-33,-42.5,0.2])
            cube([65,15,1.7]);
        }

        phone_cutouts();
    }
}

module standCutout(){
    sizeFactor=2;
    color([1,0,0])
    translate([-phone[0]/2*sizeFactor,-phone[1]/2*sizeFactor,(standLayer-1)*layerHeight])
    cube([sizeFactor*phone[0]/2,sizeFactor*phone[1],airGapLayers*layerHeight]);
}

module extraTopPocket(){
    zOffset=(airGapLayers*layerHeight)/topZ;
    color([1,0,0])
    scale([1,(phone[1]-2*seamTopPocket)/phone[1],1])
    translate([seamTopPocket,0,(extraTopPocketLayer-1)*layerHeight])
    scale([1,(phone[1]-2*seamTopPocket)/phone[1],zOffset])
    top();
}

module top(){
    scaleSizeWithWallThicknessFactor=0.75;
    translate([-1.5*phone[0]-hingeX+case_rounding-scaleSizeWithWallThicknessFactor*caseWall[0],-phone[1]/2+1.5*case_rounding-scaleSizeWithWallThicknessFactor*caseWall[1],0])
    difference(){
        minkowski(){
            cube([phone[0]-2*1.5*case_rounding-case_rounding_sphere+2*scaleSizeWithWallThicknessFactor*caseWall[0],phone[1]-2*1.5*case_rounding+2*scaleSizeWithWallThicknessFactor*caseWall[1],topZ]);
            cylinder(r=case_rounding*1.5,h=case_rounding);
        }
        translate([-case_rounding*1.5-OVERLAP/2,-case_rounding*1.5-OVERLAP/2,topZ])
        cube([phone[0]+OVERLAP+case_rounding_sphere+caseWall[0],phone[1]+OVERLAP+2*caseWall[1],100]);
    }
}

module creditCardPockets(){
      translate([-phone[0]-hingeWidth-caseWall[0]/2,-phone[1]/2+creditCardSpace[0]/2+creditCardPocketSeam+case_rounding,topZ])
      {
      for (i=[1:numberCreditCardPockets]){
        creditCardPocket(i,numberCreditCardPockets);
      }
      }
}

module creditCardPocket(pocketNumber,totalNumber){
    yOffset=(totalNumber-pocketNumber)*creditCardPocketYOffsetPerPocket;
    extraYOffset=(totalNumber-pocketNumber)*extraYOffsetPerPocket;
    zOffset=(pocketNumber-1)*creditCardPocketHeight+creditCardPocketHeight/2;

    translate([0,yOffset/2+totalNumber/2*extraYOffsetPerPocket-extraYOffset/2,zOffset]){
        cube([creditCardSpace[0]+extraYOffset,creditCardSpace[1]+yOffset,bottomThickness], center=true);

        difference(){
            cube([creditCardSpace[0]+extraYOffset,creditCardSpace[1]+yOffset,creditCardPocketHeight], center=true);

        translate([0,creditCardPocketSeam+yOffset/2,bottomThickness-(creditCardPocketHeight-creditCardZ())/2])
            cube([creditCardSpace[0]-2*creditCardPocketSeam,creditCardSpace[1]-creditCardPocketSeam,creditCardZ()+OVERLAP], center=true);
        }
    }
}

module hinge(){
    color([1,1,0])
    translate([-phone[0]/2-hingeWidth/2,0,hingeZ/2])
    cube([hingeX+hingeAdditional+2*case_rounding+caseWall[0],phone[1]+2*caseWall[1]-case_rounding-smallerY/2,hingeZ], center=true);
}

module phone(){
    hull(){
        translate([-phone[0]/2-3.4,-phone[1]/2-3.1,-3.3-2.5])
        scale([25.85,25.85,25.85*(phone[2]+phoneZCorrection)/phone[2]])
        import(path, convexity = 10);
    }
}

module phone_cutouts(){
	// camera
	translate([camera_loc[0], camera_loc[1], -camera_loc[2]+camera_rounding])
	minkowski()
	{
		cube([camera[0]-2*camera_rounding,
			camera[1]-2*camera_rounding,
			camera[2]], center=true);
		difference()
		{
			sphere(r=camera_rounding);
			translate([-camera_rounding,-camera_rounding,0])
				cube(2*camera_rounding);
		}
	}

        phone_cutout_helper("TOP",posCutout1,topCutout);

        phone_cutout_helper("RIGHT",posCutout2,powerbutton);

        phone_cutout_helper("BOTTOM",posCutout3,bottomCutout);
}

// phoneSideAlias=LEFT/RIGHT/TOP/BOTTOM/UPPER/UNDER
// posVector=[x,y,z]
// sizeVector=[width,height,depth]
module phone_cutout_helper(phoneSideAlias,posVector,sizeVector){
    dZ=sizeVector[1]/2; // default Z offset
    if (phoneSideAlias=="LEFT"){
        offsetVector=[-phone[0]/2-posVector[1],-posVector[0],dZ+posVector[2]];
        rotateVector=[-90,0,90];
        phone_cutout(sizeVector,offsetVector,rotateVector);
    } else if (phoneSideAlias=="RIGHT"){
        offsetVector=[phone[0]/2+posVector[1],posVector[0],dZ+posVector[2]];
        rotateVector=[90,0,90];
        phone_cutout(sizeVector,offsetVector,rotateVector);
    } else if (phoneSideAlias=="TOP"){
        offsetVector=[posVector[0],phone[1]/2+posVector[1]+sizeVector[2],dZ+posVector[2]];
        rotateVector=[90,0,0];
        phone_cutout(sizeVector,offsetVector,rotateVector);
    } else if (phoneSideAlias=="BOTTOM"){
        offsetVector=[posVector[0],-phone[1]/2-posVector[1],dZ+posVector[2]];
        rotateVector=[90,0,0];
        phone_cutout(sizeVector,offsetVector,rotateVector);
    } else if (phoneSideAlias=="UPPER"){
        // TODO
    } else if (phoneSideAlias=="UNDER"){
        offsetVector=[posVector[0],phone[1]/2+posVector[1],dZ+posVector[2]];
        rotateVector=[0,180,90];
        phone_cutout(sizeVector,offsetVector,rotateVector);
    }
}

// sizeVector=[width,height,depth]
module phone_cutout(sizeVector,offsetVector,rotateVector){
    color([1,0,0])
    translate([offsetVector[0],offsetVector[1],offsetVector[2]])
    rotate([rotateVector[0],rotateVector[1],rotateVector[2]])
    translate([-sizeVector[0]/2+sizeVector[1]/6,0,0])
			hull()
			{
					cylinder(d=sizeVector[1],h=sizeVector[2]);
				translate([sizeVector[0]-sizeVector[1]/3,0,0])
					cylinder(d=sizeVector[1],h=sizeVector[2]);
			}
}

module engravedText(){
    color([1,0,0])
    translate ([engravedTextPosX,engravedTextPosY,engravedTextLayers*layerHeight])
    rotate ([0,180,0])
    linear_extrude(engravedTextLayers*layerHeight)
        text(engravedText, font = engravedTextFont, size = engravedTextSize, spacing = engravedTextSpacing );
}
```


[User]
I'm looking to create a parametric cookie cutter in the shape of a sleigh, and I'd like to be able to control several aspects of the design, particularly with 3D printing in mind. I've seen some designs use support structures integrated into the base, which is what I am trying to do.

First, the base should be a flat, extruded section that includes support structures. These supports should be generated in a grid pattern. I'd like to specify the number of base layers, how many lines to offset for the base, plus the support structure grid spacing and thickness. I'd also like to offset where the supports start from the outline of the sleigh. The supports should also be able to be rotated.

Then, there should be a cutter that follows the outline of the sleigh but is offset from the main outline to create the cutting edge. I'd want to control the height of this cutter and its thickness. And lastly I would like to scale the whole thing easily.

I would need to be able to adjust the following parameters:

*   `layerHeight`: The layer height of the 3D printer
*   `lineWidth`: The width of the extrusion line of the 3D printer.
*   `layersBase`: Number of base layers to print.
*   `linesBase`: Number of lines to offset for the base.
*   `linesThick`: How many lines to offset the cutter.
*  `layersHeight`: Number of layers to extrude for the cutter.
*   `cutterScale`: Overall scale factor for the cutter.
*   `supportXGapFactor`: Factor for the X gap between support lines.
*   `supportYGapFactor`: Factor for the Y gap between support lines.
*   `supportRotation`: Rotation of the support structure
*   `supportXOffset`: The offset of support structure on the X axis
*   `supportYOffset`: The offset of support structure on the Y axis
*   The sleigh outline, which will be provided as a polygon.

I've got the coordinates for the sleigh outline and can provide them to you directly. I'm not super familiar with vector math so I'd prefer to not mess with anything there, I'm happy to just specify the point coordinates and let OpenSCAD handle the rest.

Also, I'm hoping to keep things well organized and easy to modify in the future, so I'd like to use the BOSL2 library for its utility functions if possible. I would expect to see a module for the base supports, one for the offset cutter, and another for the overall sleigh outline, plus any other helper modules that would be useful. This should all be done in a single `.scad` file.

Here are the points and paths for the sleigh:

```
points=
            [[-25.034, -30.277], [-28.255, -29.962], [-30.659, -29.463], [-32.422, -28.73], [-33.719, -27.711], [-34.698, -26.361], [-35.266, -24.874], [-35.448, -23.341], [-35.265, -21.852], [-34.74, -20.498], [-33.895, -19.369], [-32.753, -18.556], [-31.337, -18.148], [-29.718, -18.301], [-30.471, -19.865], [-31.432, -21.586], [-31.661, -23.15], [-31.168, -24.437], [-29.966, -25.326], [-27.936, -25.715], [-24.497, -25.98], [-20.582, -26.041], [-17.128, -25.817], [-15.196, -25.212], [-14.397, -24.208], [-14.585, -23.018], [-15.615, -21.855], [-17.916, -20.849], [-19.933, -19.759], [-21.916, -18.466], [-23.963, -16.653], [-28.014, -11.794], [-31.621, -5.837], [-34.322, 0.563], [-35.899, 6.824], [-36.922, 13.992], [-37.514, 18.484], [-38.018, 19.333], [-38.921, 20.039], [-40.38, 21.441], [-41.285, 23.088], [-41.656, 24.878], [-41.517, 26.709], [-40.888, 28.476], [-39.793, 30.076], [-38.253, 31.407], [-36.291, 32.364], [-34.097, 32.684], [-31.295, 32.15], [-27.407, 30.613], [-21.951, 27.926], [-15.982, 25.01], [-11.497, 23.522], [-6.99, 23.151], [-0.955, 23.585], [3.106, 23.912], [5.881, 23.887], [7.843, 23.467], [9.466, 22.607], [10.511, 21.551], [11.53, 19.985], [12.304, 18.309], [12.61, 16.921], [12.343, 15.343], [11.587, 14.04], [10.409, 13.091], [8.879, 12.574], [7.601, 12.117], [7.612, 11.147], [8.089, 9.341], [9.945, 4.765], [11.674, 1.805], [14.047, -0.866], [17.179, -3.56], [20.32, -5.331], [23.658, -6.255], [27.379, -6.412], [30.98, -5.926], [34.002, -4.769], [36.734, -2.784], [39.463, 0.186], [41.746, 2.72], [44.125, 4.199], [46.54, 5.167], [49.426, 5.961], [52.421, 6.497], [55.161, 6.694], [57.095, 6.548], [57.5, 6.327], [57.517, 5.975], [56.34, 4.777], [53.48, 2.754], [49.722, 0.23], [47.289, -1.859], [45.739, -4.476], [44.635, -8.587], [43.636, -12.456], [42.296, -14.843], [40.193, -16.602], [36.904, -18.584], [35.406, -19.594], [34.555, -20.594], [34.252, -21.655], [34.397, -22.844], [35.4, -24.083], [36.729, -24.643], [39.807, -24.805], [45.179, -24.745], [49.261, -24.303], [52.713, -23.283], [55.047, -21.943], [56.892, -20.184], [58.877, -17.914], [60.137, -15.858], [60.558, -13.728], [60.527, -11.78], [60.803, -10.392], [61.308, -9.486], [61.965, -8.983], [63.424, -8.877], [64.564, -9.449], [64.99, -10.098], [65.28, -11.185], [65.496, -14.526], [65.329, -17.439], [64.466, -19.753], [62.748, -22.55], [60.435, -24.952], [57.554, -26.937], [54.133, -28.48], [50.559, -29.599], [45.228, -30.169], [12.61, -30.453], [-25.034, -30.277]]
,
        paths=
            [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 0]]
```

[Assistant]
sleigh.scad:
```
/* [Machine Settings] */

// Your selected layer height
layerHeight=0.2;

// Your printers line width
lineWidth=0.4;

/* [Print Settings] */

// Number of layers to print of the base
layersBase=3;

// Number of lines to print in the base
linesBase=3;

// Number of lines to print the cutter
linesThick=2;

// Number of layers tell for the cutter
layersHeight=60;

// Scale
cutterScale=1;

/* [Support Settings] */

// Support X Gap Factor
supportXGapFactor=20;

// Support Y Gap Factor
supportYGapFactor=30;

// Support Rotation
supportRotation=0;

// Support X Offset
supportXOffset=2;

// Support Y Offset
supportYOffset=0;

linear_extrude(layersBase * layerHeight)
supports();

linear_extrude(layersBase * layerHeight)
offset(linesBase * lineWidth)
sleighOutline();

linear_extrude(layersHeight * layerHeight)
sleighOutline();

module supports() {
    intersection() {
        sleigh();

        rotate([0, 0, supportRotation])
        union() {
            for (i = [-200:lineWidth * linesBase * supportXGapFactor:200]) {
                translate([i + supportXOffset, 0])
                square([lineWidth * linesBase * 2, 400], true);
            }

            for (i = [-200:lineWidth * linesBase * supportYGapFactor:200]) {
                translate([0, i + supportYOffset])
                square([400, lineWidth * linesBase * 2], true);
            }
        }
    }
}

module sleighOutline() {
    difference() {
        offset(linesThick * lineWidth)
        sleigh();

        sleigh();
    }
}

module sleigh()
    scale(cutterScale)
    polygon(
        points=
            [[-25.034, -30.277], [-28.255, -29.962], [-30.659, -29.463], [-32.422, -28.73], [-33.719, -27.711], [-34.698, -26.361], [-35.266, -24.874], [-35.448, -23.341], [-35.265, -21.852], [-34.74, -20.498], [-33.895, -19.369], [-32.753, -18.556], [-31.337, -18.148], [-29.718, -18.301], [-30.471, -19.865], [-31.432, -21.586], [-31.661, -23.15], [-31.168, -24.437], [-29.966, -25.326], [-27.936, -25.715], [-24.497, -25.98], [-20.582, -26.041], [-17.128, -25.817], [-15.196, -25.212], [-14.397, -24.208], [-14.585, -23.018], [-15.615, -21.855], [-17.916, -20.849], [-19.933, -19.759], [-21.916, -18.466], [-23.963, -16.653], [-28.014, -11.794], [-31.621, -5.837], [-34.322, 0.563], [-35.899, 6.824], [-36.922, 13.992], [-37.514, 18.484], [-38.018, 19.333], [-38.921, 20.039], [-40.38, 21.441], [-41.285, 23.088], [-41.656, 24.878], [-41.517, 26.709], [-40.888, 28.476], [-39.793, 30.076], [-38.253, 31.407], [-36.291, 32.364], [-34.097, 32.684], [-31.295, 32.15], [-27.407, 30.613], [-21.951, 27.926], [-15.982, 25.01], [-11.497, 23.522], [-6.99, 23.151], [-0.955, 23.585], [3.106, 23.912], [5.881, 23.887], [7.843, 23.467], [9.466, 22.607], [10.511, 21.551], [11.53, 19.985], [12.304, 18.309], [12.61, 16.921], [12.343, 15.343], [11.587, 14.04], [10.409, 13.091], [8.879, 12.574], [7.601, 12.117], [7.612, 11.147], [8.089, 9.341], [9.945, 4.765], [11.674, 1.805], [14.047, -0.866], [17.179, -3.56], [20.32, -5.331], [23.658, -6.255], [27.379, -6.412], [30.98, -5.926], [34.002, -4.769], [36.734, -2.784], [39.463, 0.186], [41.746, 2.72], [44.125, 4.199], [46.54, 5.167], [49.426, 5.961], [52.421, 6.497], [55.161, 6.694], [57.095, 6.548], [57.5, 6.327], [57.517, 5.975], [56.34, 4.777], [53.48, 2.754], [49.722, 0.23], [47.289, -1.859], [45.739, -4.476], [44.635, -8.587], [43.636, -12.456], [42.296, -14.843], [40.193, -16.602], [36.904, -18.584], [35.406, -19.594], [34.555, -20.594], [34.252, -21.655], [34.397, -22.844], [35.4, -24.083], [36.729, -24.643], [39.807, -24.805], [45.179, -24.745], [49.261, -24.303], [52.713, -23.283], [55.047, -21.943], [56.892, -20.184], [58.877, -17.914], [60.137, -15.858], [60.558, -13.728], [60.527, -11.78], [60.803, -10.392], [61.308, -9.486], [61.965, -8.983], [63.424, -8.877], [64.564, -9.449], [64.99, -10.098], [65.28, -11.185], [65.496, -14.526], [65.329, -17.439], [64.466, -19.753], [62.748, -22.55], [60.435, -24.952], [57.554, -26.937], [54.133, -28.48], [50.559, -29.599], [45.228, -30.169], [12.61, -30.453], [-25.034, -30.277]],
        paths=
            [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 0]]
    );

```


[User]
I want to make a customizable seahorse design that I can print. I'd like to be able to control the overall size, the height of the model, and the thickness of both an outer and inner outline. I need the design to be 2D, with the inner outline cut out to leave a silhouette.

Specifically, I'd like to be able to set:

*   `max_size_in_millimeter`: The length of the longest side of the seahorse (the other dimension should scale proportionally to maintain aspect ratio).
*   `model_height_in_millimeter`: The height of the 3D model (the z-dimension).
*   `outline_size_in_millimeter`: The thickness of the outer outline.
*   `inline_size_in_millimeter`: The thickness of the inner outline.
*   `inline_edge_radius_in_millimeter`: How rounded the ends of the inner outline should be.
* `flip_model`: A yes/no flag to mirror the model over its horizontal axis.

The design should be based on the provided coordinates for the outer shape and the inner cutouts, making sure that the inner cuts are inset from the outer edges, leaving an outline. The provided coordinates are:

```
// Outer Points
A1 = [630, 499];
A2 = [778, 466];
A3 = [788, 393];
A4 = [985, 323];
A5 = [1112, 530];
A6 = [1060, 654];
A7 = [1070, 686];
A8 = [1134, 669];
A9 = [1242, 773];
A10 = [1187, 900];
A11 = [1124, 918];
A12 = [1133, 958];
A13 = [1134, 1165];
A14 = [988, 1274];
A15 = [892, 1277];
A16 = [837, 1190];
A17 = [890, 1233];
A18 = [986, 1220];
A19 = [1049, 1133];
A20 = [1046, 1060];
A21 = [859, 742];
A22 = [859, 606];
A23 = [901, 532];
A24 = [770, 532];
A25 = [630, 532];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25];

// Polygons
C1 = [A25, A2, A24];
C2 = [A1, A25, A2];
C3 = [A3, A4, A5, A23, A24, A2];
C4 = [A5, A6, A21, A22, A23];
C5 = [A6, A7, A11, A12, A20, A21];
C6 = [A7, A8, A9, A10, A11];
C7 = [A12, A13, A19, A20];
C8 = [A19, A13, A14, A18];
C9 = [A17, A18, A14, A15];
C10 = [A16, A17, A15];

cut_polygons = [C1, C2, C3, C4, C5, C6, C7, C8, C9, C10];
```

Please generate the OpenSCAD code. I don't need any libraries.

[Assistant]
customizable_origami_seahorse.scad:
```
/*
 * Customizable Origami - Seahorse - https://www.thingiverse.com/thing:2704265
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-12-10
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

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

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.8; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.8; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.5; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [630, 499];
A2 = [778, 466];
A3 = [788, 393];
A4 = [985, 323];
A5 = [1112, 530];
A6 = [1060, 654];
A7 = [1070, 686];
A8 = [1134, 669];
A9 = [1242, 773];
A10 = [1187, 900];
A11 = [1124, 918];
A12 = [1133, 958];
A13 = [1134, 1165];
A14 = [988, 1274];
A15 = [892, 1277];
A16 = [837, 1190];
A17 = [890, 1233];
A18 = [986, 1220];
A19 = [1049, 1133];
A20 = [1046, 1060];
A21 = [859, 742];
A22 = [859, 606];
A23 = [901, 532];
A24 = [770, 532];
A25 = [630, 532];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25];

// Polygons
C1 = [A25, A2, A24];
C2 = [A1, A25, A2];
C3 = [A3, A4, A5, A23, A24, A2];
C4 = [A5, A6, A21, A22, A23];
C5 = [A6, A7, A11, A12, A20, A21];
C6 = [A7, A8, A9, A10, A11];
C7 = [A12, A13, A19, A20];
C8 = [A19, A13, A14, A18];
C9 = [A17, A18, A14, A15];
C10 = [A16, A17, A15];

cut_polygons = [C1, C2, C3, C4, C5, C6, C7, C8, C9, C10];

min_x = A1[0];
min_y = A4[1];
max_x = A9[0];
max_y = A15[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
I need to create a cutting mat for my desk, similar to one I already made, but I want it to be parametric so I can easily adjust the size and the little "feet" on the bottom.

My current design uses a simple minkowski sum of a cube and a cylinder to create a flat mat with rounded feet. The base of the mat is a rectangular cube and the feet are rounded with a cylinder. I would like a configurable option for the height of the mat, the width, and the length. I'd also like to be able to control the radius of the cylinder, and the height that the feet protrude past the bottom of the mat. Additionally, I would like a configurable option to change the spacing between the feet - I'd like to specify the number of feet in the x and y directions, respectively, and have them evenly spaced. If the spacing is set too high so that the feet would overlap, there should be an option to either increase the size of the overall mat, or simply decrease the spacing to prevent overlap. For reference, the code I used is included as `cuttingMat.scad`. The new design should include a module for generating the mat itself and should not be a top level operation.

Could you generate an OpenSCAD file for me that will handle this, please? I don't need any specific library beyond the base OpenSCAD functionality.

[Assistant]
cuttingMat.scad:
```
width=100;
length=100;
height=2;
/* [HIDDEN] */
$fn=150;
actualHeight=(height-1);
minkowski()
{
  cube([width,length,ActualHeight]);
  cylinder(r=2,h=1);
}
```