
I am going to ask you to generate 2D CAD programs for making laser cut objects in a python DSL called AIDL. 
First I will describe the AIDL language, then I will give you some example AIDL programs. After that I will 
guide you step-by-step through generating and AIDL program for a particular object.


AIDL is a python DSL for writing constrained 2D CAD models suitable for laser cutting. AIDL models are trees where each node, called a Structure, represents a subpart of the object in the form of a 2D constrained CAD sketch.

Structures come in 4 kinds: Solids, Holes, Drawings, and Assemblies. Solids and Holes hold sketches with edges that will be cut all the way through the board by the laser cutter. Only edges which form closed contours (faces) are used in Solids and Holes, but how these faces are used differs between them. Solid faces are treated as positive and are boolean unioned together. Hole faces are treated as negative, and boolean subtracted from the positive faces after they have been joined by union. This process in recursive up the tree.

Edges in Drawing nodes are not joined into faces, and are just interpreted as engraving lines for the laser cutter. They are used to add graphic details to the laser cut objects.

Sketches on Assembly nodes do not show up at all in the laser cutting. They are just used as construction geometry to help position other sketches using Constraints. Assembly nodes do not union the faces of their children, but rather treat them as individual objects to be cut out.

In order to be a valid model, Assembly nodes can only be the children of other Assembly nodes and Drawings can only have other Drawings as children.

Each structure node in the model tree has a 2d shift from its parent that translates the whole structure. So each node has its own coordinate origin that the geometry on it is defined relative to. Structure nodes always have a semantic name relative to their parent. This is defined when attached a node to its parent because the tree is created using python assignment, so nodes have the variable name assigned to them:

```python
model = Solid() # Create a Solid Structure
model.my_child = Solid(shift=(1*inch, 0)) # Create a child Solid node of model called "my_child" centered 1 inch to the right
```

The sketches on each node are specified by attaching 2D Geometry objects to the structure nodes. Geometry objects are either geometric primitives, Point, Line, Circle, or Arc, or they are compound geometric objects: Rectangle, Rounded Rectangle, Triangle, or RightTriangle. Geometry objects also have names, and are attached to structures just like other structures are:

```python
frisbee = Solid() # Create a solid "frisbee"
frisbee.disk = Circle( (0,0), 4*inch) # add a Circle at the origin with a 4 inch radius to frisbee and call it "disk"
```

Structure children and geometry can also be added into collections (lists and dictionaries):
```python
many_circles = Solid()
many_circles.circles = [Circle((i, 0), 0.5) for i in range(10)] # Creates 10 circles with the names "circles[0]", "circles[1]", ... on the many_circles Solid.
```

The 4 geometric primitives and initialization signatures are: 

Point(Coords)
Line(start: Coords | Point, end: Coords | Point)
Circle(center: Coords | Point, radius: float)
Arc(center: Coords | Point, start: Coords | Point, end: Coords : Point) # A counterclockwise semicircular arc around center from start to end.

Arc has a few other 
Arc(center: Coords | Point, start: Coords | Point, angle: float) #  An arc around Point center starting at Point start and going counterclockwise for angle degrees
Arc(center: Coords | Point, angle: float, end: Coords | Point) # An angle degree counterclocwise arc around center ending at Point end
Arc(center: Coords | Point, start_angle: float, end_angle: float, radius: float) # A counterclockwise semicircular arc acount center with radius radius from start_angle to end_angle. Angles in degrees, 0 points right, and angles increase counterclockwise

In these Coords = (u: float, v: float). Primitives can either use float values to define their initial positions, or references to an existing Point. If they use a reference, then the same Point instance is used in the Geometry object.

Geometry objects sometimes have have named property references to other Geometry:

Line:
start: Point
end: Point
midpoint: Point

Circle:
center: Point

Arc:
center: Point
start: Point
end: Point

In addition to the primitive geometry, there are 4 kinds of compound geometry that combine multiple primitives plus constraints:

Rectangle(center: Coords | Point, width: float, height: float, axis_aligned: bool = True)
RoundedRectangle(center: Coords | Point, width: float, height: float, corner_radius: float, axis_aligned: bool = True)
Triangle(center: Coords | Point, base_width: float, height: float)
RightTriangle(center: Coords|Point, width: float, height: float)

Compound Geometry also has named Geometry references:

Rectangle:
center: Point
top_right: Point
top_left: Point
bottom_right: Point
bottom_left: Point
top: Line
bottom: Line
right: Line
left: Line

RoundedRectangle:
center: Point
top: Line
bottom: Line
left: Line
right: Line
top_left: Arc
top_right: Arc
bottom_left: Arc
bottom_right: Arc

Triangle:
top_corner: Point
left_corner: Point
right_corner: Point
base: Line
left_side: Line
right_side: Line

RightTriangle:
base: Line
hypotenuse: Line
rise: Line


Sketches and structures can also be constrained. Constraints are added to a structure using the AddConstraint method. Constraints on a structure can relate any descendant structures or Geometry, but cannot relate geometry or structures closer to the model root.

There are 2 kinds of Constraints; geometric constraints that relate two pieces of geometry, and equational constraints that can relate geometry or structures.

The Geometry constraints are:

Coincident(point1: Point, point2: Point)
Coincident(point: Point, line: Line)
Coincident(point: Point, circle: Circle)
Coincident(line1: Line, line2: Line)
Coincident(circle1: Circle, circle2: Circle)
Coincident(circle: Circle, arc: Arc)
Coincident(arc1: Arc, arc2: Arc)
Coincident(point: Point, arc: Arc)

Distance(point1: Point, point2: Point, distance: float)
Distance(point: Point, line: Line, distance: float)
Distance(point: Point, circle: Circle, distance: float)

Equal(line1: Line, line2: Line)
Equal(line: Line, arc: Arc)
Equal(line: Line, circle: Circle)
Equal(circle: Circle, arc: Arc)
Equal(circle1: Circle, circle2: Circle)
Equal(arc1: arc, arc2: Arc)


EqualLength(line1: Line, line2: Line)
EqualLength(line: Line, arc: Arc)
EqualLength(line: Line, circle: Circle)

EqualRadius(circle: Circle, arc: Arc)
EqualRadius(circle1: Circle, circle2: Circle)
EqualRadius(arc1: arc, arc2: Arc)

EqualAngle(A1: Line, B1: Line, A2: Line, B2: Line)
EqualAngle(corner1: Point, a1: Point, b1: Point, corner2: Point, a2: Point, b2: Point)
EqualAngle(A1_start: Point, A1_end: Point, B1_start: Point, B1_end: Point, A2_start: Point, A2_end: Point, B2_start: Point, B2_end: Point)
EqualAngle(angle1: tuple[Point, Point, Point], angle2: tuple[Point, Point, Point])
EqualAngle(A1: tuple[Point, Point], B1: tuple[Point, Point], A2: tuple[Point, Point], B2: tuple[Point, Point])

EqualPointLineDistance(pointA: Point, lineA: Line, pointB: Point, lineB: Line)

LengthRatio(line1: Line, line2: Line, ratio: float)
LengthRatio(line1: tuple[Point, Point], line2:[Point, Point], ratio: float)
LengthRatio(p11: Point, p12: Point, p21: Point, p22: Point, ratio: float)

Symmetric(A: Point, B: Point, line: Line)

HorizontallySymmetric(A: Point, B: Point)
VerticallySymmetric(A: Point, B: Point)

AtMidpoint(point: Point, line: Line)
AtMidpoint(line: Line, point: Point)
AtMidpoint(line: tuple[Point, Point], point: Point)
AtMidpoint(point: Point, line: tuple[Point, Point])
AtMidpoint(point1: Point, point2: Point, point3: Point)

Horizontal(line: Line)
Horizontal(point1: Point, point2: Point)

Vertical(line: Line)
Vertical(point1: Point, point2: Point)

Diameter(circle: Circle, diameter: float)
Diameter(arc: Arc, diameter: float)


Perpendicular(line1: Line, line2: Line)
Parallel(line1: Line, line2: Line)

Tangent(line: Line, arc: Arc)
Tangent(arc: Arc, line: Line)
Tangent(arc1: Arc, arc2: Arc)

LengthDifference(line1: Line, line2: Line, difference: float)


As an example, a two circles can be constrained to have same center:

```python
model = Solid()
model.circle1 = Circle((-2,3), 5) # initialize to center at -2mm, 3mm
model.circle2 = Circle((2.5,5), 6) # initialize to center at 2.5mm, 5mm

# Constrain the circles to have coincident centers:

# Circles have a reference called "center" to the Point they are centered on.
model.AddConstraint(Coincident(circle1.center, circle2.center))

```

Constraints are interpreted in the coordinate frame of the structure they are attached to. So in
```python
model = Solid()
model.sub = Solid(shift=(5,0)) # creates a new coordinate system shifted 5 mm to the right
model.p1 = Point(4,0) # p1 is at (4,0) in the frame of model
model.sub.p2 = Point(-5,0) # p2 is at (-5, 0) in the frame of model.sub

# Add a horizontally symmetric constraint on model between model.p1 and model.sub.p2
# Because this constraint is on model, the points are constrained to be at equal vertical
# positions and opposite horizontal positions relative to the origin of model, not model.sub
model.AddConstraint(HorizontallSymmetric(model.p1, model.sub.p2))

```

As a complement to Geometry constraints, AIDL also supports Equation constraints for equality and inequality constraints.

AIDL supports expressions involving:
- Arithmetic: (+, -, *, /)
- Square and square root: sqrt(expr), expr**2
- sin, cos, asin, and acos: sin(expr), cos(expr), asin(expr), acos(expr)
- Equalities and inequalities: ==, <=, >=
- absolute value: abs(expr)
- minimum and maximum: minimum(expr1, expr2, expr3, ...), maximum(expr1, expr2, expr3, ...)
- logical_and: (eq1, eq2, eq3, ...)
- degree and radians conversions: degrees(rads), radians(degs)

Geometry and Structure nodes have named Expression properties for use in constraints.

On Geometry:

Point: u, v
Line: length
Circle: radius
Arc: radius
Rectangle: width, height
RoundedRectangle: width, height, corner_radius


Every structure has a proprerty call bb that represents its axis-aligned bounding box.
These store Expressions for the coordinates the left, right, top, and bottom sides of
this bounding box, the horizontal and vertical centers, and the height and width:

bb.top # max v coordinate
bb.bottom # min v coordinate
bb.left # min u coordinate
bb.right # max u coordinate
bb.width # bb.right - bb.left
bb.height # bb.top - bb.bottom
bb.horizontal_center # (bb.left + bb.right) / 2
bb.vertical_center # (bb.top + bb.bottom) / 2

When bounding box coordinates are used in a constraint expression, they are evaluated in the coordinate system of the Structure that the Constraint is on (i.e. the Structure-local bounding box).

Bounding box coordinates are useful for positioning and sizing structures relative to each other, these types of constraints are called Compositional Constraints.

Equation constraints are added by calling AddConstraint with the constraint equation:

```python

model = Solid()

model.circ = Circle((0,0), 4)

model.sub1 = Solid()
model.sub1.line = Line((-1,0),(1,1))

model.sub2 = Solid()
model.sub2.rect = Rectangle((0,0), 5, 2)

# the diameter of the circle is equal to the length of the line
model.AddConstraint(2*model.circ.radius == model.sub1.line.length)

# sub1 is directly on top of and left aligned with sub2
model.AddConstraint(logical_and(
    model.sub1.bb.bottom == model.sub2.bb.top, # sub1 is atop sub2
    model.sub1.bb.left == model.sub2.bb.left # sub1 and sub2 are left-aligned
))

```

When AIDL models are run, they are solved recursively, so it is best practice to divide objects into many sub structures for more efficient solving.

Since AIDL models are solved recursively, constraints are only allowed to reference Structures or Geometry within the subtree of the Structure they are added to. Constraints cannot reference Geometry or Structures which are not part of the model tree.

When I guide you through generating an AIDL program, I will first ask you to generate the structures and their hierarchical relationships. Then I will ask you to generate constraints between the structures. Then I will ask you to generate the geometry for each structure, one at a time. At each generation step, I will give feedback if the AIDL program is invalid.

That's it for the language definition. Next I will show you several example programs


When writing an AIDL program, the first line, and ONLY import statement should be

```python
from AIDL import *
```

This will import everything needed for the AIDL, including python's math library, into the global namespace.

AIDL programs should be written as python functions that take as input any design parameters (with reasonable defaults),
and return the Structure node for the root of the model.

After the function definition, create a model, solve it, and render it, using the following snippet

```python

with AIDL_2D:
    model = {model_definition_function}()
print(model.Solve())
model.Render()
```

where {model_definition_function} is the python function you defined that returns the root of the model.


Each of the examples below follow this format.



Writes an AIDL program to generate a bottle opener

```python
from AIDL import *

def bottle_opener(
        height: float = 5 * inches,
        bottle_cap_diameter = 1*inch # standard US bottle cap size
):
    """
    Model a simple rectangular bottle opener with mouth shaped as a
    segment of a circle.
    """

    # First determine the width of the bottle opener based on the size
    # of bottle caps to open. Add 50% so there is space for the hole
    width = 1.5 * bottle_cap_diameter

    # Define the model structure. It has two strutures, a Solid bottle
    # opener with one Hole for the mouth.
    bottle_opener = Solid()
    mouth = Hole()
    bottle_opener.mouth = mouth

    # Define the body geometry. It is a simple rounded rectangle with 
    # corner radius proportional to the width
    bottle_opener.body = RoundedRectangle((0,0), width, height, width/6)

    # Define the mouth geometry. It is the major segment of a circle, so
    # the top lip is an Arc, and the bottom is a line
    mouth.top_lip = Arc((0,2*inch),(.5*inch,1*inch), (-.5*inch,0))
    mouth.bottom_lip = Line(mouth.top_lip.start, mouth.top_lip.end)

    # Determine the size of the bottle opener's mouth based on the size of
    # bottle caps the opener is designed to work on. It needs to be wide
    # enough for a bottle cap to fully fit inside horizontally, but also
    # short enough so that the cap can be wedged between the top and bottom
    # lips. The wider the bottom lip is, the shorter the hole will be;
    # 0 length would make the hole as wide as it is tall, and a bottle
    # cap diameter would make it around half the diameter of a bottle cap.
    # Choose a value halfway between these (3/4 bottle cap diameter) to
    # make it easy to wedge the cap without making it too hard to fit in
    # the hole.
    mouth.AddConstraint(mouth.bottom_lip.length == .75*bottle_cap_diameter) # Make 3/4 bottle cap diameter so the cap cannot fit through vertically
    mouth.AddConstraint(mouth.top_lip.radius*2 == 1.05*bottle_cap_diameter) # add 5% tollerance for fitting cap in to mouth
    # Making the bottom lip horizontally symmetric both centers the mouth and keeps the bottom lip horizontal
    mouth.AddConstraint(HorizontallySymmetric(mouth.bottom_lip.start, mouth.bottom_lip.end))

    return bottle_opener

with AIDL_2D:
    model = bottle_opener()
print(model.Solve())
model.Render()

```



Writes an AIDL program to generate a ruler

```python
from AIDL import *

def ruler():
    ruler = Solid()
    binder_holes = Hole()
    imperial_scale = Drawing()
    metric_scale = Drawing()
    scales = Drawing()
    scales.imperial_scale = imperial_scale
    scales.metric_scale = metric_scale
    ruler.scales = scales
    ruler.binder_holes = binder_holes


    # Make the ruler stick just large enough to hold the scales
    ruler.AddConstraint(scales.bb.top == ruler.bb.top) # Top Aligned
    ruler.AddConstraint(scales.bb.bottom == ruler.bb.bottom) # Bottom Aligned
    ruler.AddConstraint(scales.bb.left == ruler.bb.left) # Left Aligned
    ruler.AddConstraint(scales.bb.right == ruler.bb.right) # Right Aligned

    ruler.stick = Rectangle((0,0), 12*inches, 2*inches)

    imperial_scale.ticks = [Line((i*inches,1),(i*inches,1*inch-1*cm)) for i in range(13)]
    for tick in imperial_scale.ticks:
        imperial_scale.AddConstraint(Vertical(tick))
        imperial_scale.AddConstraint(tick.length == 1*cm)
    for i in range(12):
        left_tick = imperial_scale.ticks[i]
        right_tick = imperial_scale.ticks[i+1]
        imperial_scale.AddConstraint(Distance(left_tick.start, right_tick.start, 1*inch))

    metric_scale.ticks = [Line((i*cm,-1),(i*cm,1*cm - 1*inch)) for i in range(31)]
    for tick in metric_scale.ticks:
        metric_scale.AddConstraint(Vertical(tick))
        metric_scale.AddConstraint(tick.length == 1*cm)
    for i in range(30):
        left_tick = metric_scale.ticks[i]
        right_tick = metric_scale.ticks[i+1]
        metric_scale.AddConstraint(Distance(left_tick.start, right_tick.start, 1*cm))

    # Make 3 holes for binder rings, 4.25" apart
    binder_holes.left_hole = Circle((-4.25*inches, 0), inch/8)
    binder_holes.center_hole = Circle((0,0), inch/8)
    binder_holes.right_hole = Circle((4.25*inches,0), inch/8)
    # center with the ruler body
    ruler.AddConstraint(ruler.bb.horizontal_center == binder_holes.bb.horizontal_center) # horizontally aligned
    ruler.AddConstraint(ruler.bb.vertical_center == binder_holes.bb.vertical_center) # vertically aligned
    

    scales.AddConstraint(metric_scale.bb.top + 0.3*inch == imperial_scale.bb.bottom) # .3 inch spacing between scales to make room for binder holes

    return ruler


with AIDL_2D:
    model = ruler()
print(model.Solve())
model.Render()

```




Writes an AIDL program to generate a hanger

```python
from AIDL import *

def hanger(thickness: float = 0.5*inches,
            hook_radius: float = 2*inches,
            shoulder_width: float = 18*inches,
            shoulder_height: float = 6*inches):
    """
    Construct a coat hanger model. The coat hanger has a triangular body
    with a triangular hole in its center. Above the hanger is a semi-circular
    hook. The hook and body have the same thickness.
    """

    hanger = Solid()
    hook = Solid()
    body = Solid()
    hollow = Hole()

    hanger.hook = hook
    hanger.body = body
    body.hollow = hollow

    # Define the hook geometry. The top of the hook are two concentric arcs that are
    # thinkness apart in radius, capped by a small arc. The lower part, or neck,
    # Is made of a rectangle (lower_neck) that connects via arcs to the upper hook
    hook.inner_arc = Arc((0,0), 0, 180, hook_radius)
    hook.outer_arc = Arc((0,0), 0, 180, hook_radius + thickness)
    hook.tip = Arc((-hook_radius - thickness/2, 0), hook.outer_arc.end, hook.inner_arc.end)
    hook.lower_neck = Rectangle((thickness,-1*inch), thickness, hook_radius, axis_aligned=False)    
    hook.upper_neck_inner = Arc((0,0), hook.lower_neck.top_left, hook.inner_arc.start)
    hook.upper_neck_outer = Arc((0,0), hook.lower_neck.top_right, hook.outer_arc.start)

    # Define the hook constraints. These enforce the thickness of the hook, the angle
    # subtended by the hook, and constrain the outer curves to be smooth and continuous
    
    # Ensure that the hook arcs are concentric
    hook.AddConstraint(Coincident(hook.inner_arc.center, hook.outer_arc.center))
    # Make sure the hook is a constrant thickness throughout
    hook.AddConstraint(Distance(hook.outer_arc.start, hook.inner_arc.start, thickness))
    hook.AddConstraint(Distance(hook.outer_arc.end, hook.inner_arc.end, thickness))
    hook.AddConstraint(hook.lower_neck.width == thickness)
    # Constrain the starts and ends of the hook to be on the same horizontal line
    # so that the hook is always a top half-circle. Also center the hook locally.
    hook.AddConstraint(HorizontallySymmetric(hook.inner_arc.start, hook.inner_arc.end))
    hook.AddConstraint(HorizontallySymmetric(hook.outer_arc.start, hook.outer_arc.end))
    # Add tangent constraintns between edge segments to make the boundary smooth
    hook.AddConstraint(Tangent(hook.tip, hook.outer_arc))
    hook.AddConstraint(Tangent(hook.tip, hook.inner_arc))
    hook.AddConstraint(Tangent(hook.lower_neck.left, hook.upper_neck_inner))
    hook.AddConstraint(Tangent(hook.lower_neck.right, hook.upper_neck_outer))
    
    # Add conveniently named references for the lower neck points for connecting to the
    # body later.
    hook.connection_inner = hook.lower_neck.bottom_left
    hook.connection_outer = hook.lower_neck.bottom_right

    # Define the body geometry. It consists of a simple triangle
    body.shoulders = Triangle((0,0), shoulder_width, shoulder_height)

    # Define the local body constraints:
    # Constrain the body dimensions
    body.AddConstraint(body.width == shoulder_width)
    body.AddConstraint(body.height == shoulder_height)
    # Keep the triangle fully horizontally symmetric
    body.AddConstraint(HorizontallySymmetric(body.shoulders.left_corner, body.shoulders.right_corner))
    body.AddConstraint(body.shoulders.top_corner.u == 0.) # keep the top corner centered
    
    # Define the hollow geometry
    hollow.hole = Triangle((0,0), shoulder_width - thickness, shoulder_height - thickness)

    # Define the local hollow constraints:
    # keep the triangle horizontally symmetric
    hollow.AddConstraint(HorizontallySymmetric(hollow.hole.left_corner, hollow.hole.right_corner))
    hollow.AddConstraint(hollow.hole.top_corner.u == 0) # top corner horizontally symmetric (at 0)
    
    # Global Constraints in the Body substructure
    # keep a fixed offset of 1cm between the corresponding sides of the body and the hollow
    body.AddConstraint(Distance(body.shoulders.left_side, hollow.hole.top_corner, thickness))
    body.AddConstraint(Distance(body.shoulders.right_side, hollow.hole.top_corner, thickness))
    body.AddConstraint(Parallel(body.shoulders.right_side, hollow.hole.right_side))
    body.AddConstraint(Parallel(body.shoulders.left_side, hollow.hole.left_side))
    body.AddConstraint(Distance(body.shoulders.base, hollow.hole.left_corner, thickness))

    # Global Constraints for the whole hanger

    # constrain the hook connection points to be on the top and right side
    hanger.AddConstraint(Coincident(hook.connection_inner, body.shoulders.top_corner))
    hanger.AddConstraint(Coincident(hook.connection_outer, body.shoulders.right_side))
    # horizontally align the hook and body
    hanger.AddConstraint(hook.bb.horizontal_center == body.bb.horizontal_center)

    return hanger


with AIDL_2D:
    model = hanger()
print(model.Solve())
model.Render()

```



Writes an AIDL program to generate a key

```python
from AIDL import *

def key(bow_radius=0.5*inch, shank_length=4*inches, shank_width=.25*inch, bit_width=.5*inch, bit_depth=.5*inch, wards = [.5, .3]):
    """
    Creates a old-style warded key with one bit and rectangular wards in the direction of the shank.

    The key is rendered with the bow on the left, shank on the right, and the bit below the shank.
    This orientation means that shank length is a horizontal dimension, and shank_width is vertical (e.g. height)

    The collar is on the left of the bit.

    Parameter `wards` is a list of ward depths expressed as fraction of the bit width
    """

    # Define the composition of the key. It has a bow with a loop hole in it, a horizaontal shank,
    # and a bit with wards cut out of it.
    key = Solid()
    key.bow = Solid()
    key.bow.loop = Hole()
    key.shank = Solid()
    key.collar = Solid()
    key.bit = Solid()
    key.bit.wards = Hole()

    # Define the compositional constraints that relate structures in a key
    key.AddConstraint(key.bit.bb.top == key.shank.bb.bottom) # The bit abuts the shank on the bottom
    key.AddConstraint(key.bow.loop.bb.right == key.shank.bb.left) # the shank extends left into the bow up until the loop
    key.AddConstraint(key.shank.bb.width == shank_length) # the shank is `shank_length` long (horizontal dimension)
    key.AddConstraint(key.shank.bb.height == shank_width) # the girth of the shank (vertical in this rendering) is shank_width
    key.AddConstraint(key.bit.bb.right == key.shank.bb.right - shank_width) # leave a post of one shank width

    # Fix the input dimensions of the bit
    key.bit.AddConstraint(key.bit.bb.width == bit_width)
    key.bit.AddConstraint(key.bit.bb.height == bit_depth)

    # Align the wards with the bit
    key.bit.AddConstraint(key.bit.wards.right == key.bit.right) # wards extend to the right side of the bit
    key.bit.AddConstraint(key.bit.wards.top == key.bit.top) # wards start at the top of the bit (shank side)
    # wards are inside the bit on left and bottom sides
    key.bit.AddConstraint(key.bit.wards.left >= key.bit.left)
    key.bit.AddConstraint(key.bit.wards.bottom >= key.bit.bottom)

    # Position and size the collar with respect to the bit and shank
    key.AddConstraint(key.collar.bb.right == key.bit.bb.left - shank_width) # collar is spaced one shank_width left of the bit
    key.AddConstraint(key.collar.bb.vertical_center == key.shank.bb.vertical_center) # collar and shank are vertically centered with each other
    key.collar.AddConstraint(key.collar.bb.height == 1.5*shank_width)
    key.collar.AddConstraint(key.collar.bb.width == 0.5*shank_width)

    # The loop is centered within the bow
    key.bow.AddConstraint(key.bow.bb.horizontal_center == key.bow.loop.bb.horizontal_center) # loop horizontally centered in bow
    key.bow.AddConstraint(key.bow.bb.vertical_center == key.bow.loop.bb.vertical_center) # loop vertically centered in bow

    # The bow and the shark are vertically aligned
    key.AddConstraint(key.shank.bb.vertical_center == key.bow.bb.vertical_center)

    # The shaft of the shank is a rounded rectangle
    key.shank.shaft = RoundedRectangle((0,0), shank_length, shank_width, shank_width / 3)

    key.collar.shoulder = RoundedRectangle((0,0), shank_width/2, 1.5*shank_width, shank_width/5)

    # Make a rectangular blank for the bit
    key.bit.blank = Rectangle((0,0), bit_width, bit_depth)
    # Add wards to the blank as rectangular holes
    ward_depth = bit_depth / (2*len(wards)) # wards and blanks between wards have equal depth, and they evenly 
    key.bit.wards.cutouts = []
    for i, width_pct in enumerate(wards):
        ward_width = bit_width * width_pct
        key.bit.wards.cutouts.append(Rectangle((- ward_width/2, 2*i*ward_depth), ward_width, ward_depth))

    key.bow.disk = Circle((0,0), bow_radius)
    key.bow.loop.disk = Circle((0,0), bow_radius - shank_width)

    return key


with AIDL_2D:
    model = key()
print(model.Solve())
model.Render()

```



Writes an AIDL program to generate a toy sword

```python
from AIDL import *

def toy_sword():
    """
    Create a model of a toy wooden sword with laser engraved details on the blade.
    """
    sword = Solid()

    blade = Solid()
    blade_details = Drawing()
    blade.blade_details = blade_details

    hilt = Solid()
    cross_guard = Solid()
    grip = Solid()

    pommel = Solid(shift=(0,-4*inches))
    jewel_setting = Hole()
    pommel.jewel_setting = jewel_setting

    hilt.cross_guard = cross_guard
    hilt.grip = grip
    hilt.pommel = pommel

    sword.blade = blade
    sword.hilt = hilt

    # Compositional Constraints
    sword.AddConstraint(hilt.horizontal_center == blade.horizontal_center)

    blade.AddConstraint(blade.height == 22*inches)
    blade.AddConstraint(blade.width == 2.5*inches)

    blade.AddConstraint(blade_details.bb.horizontal_center == blade.bb.horizontal_center)

    grip.AddConstraint(grip.width == 1.5*inches)
    grip.AddConstraint(grip.height == 4*inches)

    pommel.AddConstraint(pommel.width == 2*inches)
    pommel.AddConstraint(pommel.height == 2*inches)

    cross_guard.AddConstraint(cross_guard.width == 6*inches)
    cross_guard.AddConstraint(cross_guard.height == 1*inch)
    
    hilt.AddConstraint(grip.horizontal_center == cross_guard.horizontal_center)
    hilt.AddConstraint(grip.horizontal_center == pommel.horizontal_center)

    sword.AddConstraint(blade.bb.bottom == hilt.bb.top)
    hilt.AddConstraint(cross_guard.bb.bottom == grip.bb.top)
    hilt.AddConstraint(pommel.bb.horizontal_center == grip.bb.horizontal_center)

    hilt.AddConstraint(pommel.horizontal_center == jewel_setting.horizontal_center)
    hilt.AddConstraint(pommel.vertical_center == jewel_setting.vertical_center)

    # Blade Geometry
    
    blade.tip = Point(0, 1*foot)
    blade.center = Rectangle((0,0), 3*inches, 18*inches)
    blade.AddConstraint(Vertical(blade.center.left))
    blade.left_taper = Line(blade.tip, blade.center.top_left)
    blade.right_taper = Line(blade.tip, blade.center.top_right)
    
    # Blade Detail Geometry
    blade_details.fuller = Line((0,-8*inch),(0,8*inch))
    blade_details.fuller_left = Line(blade_details.fuller.end, (-1.5*inch, 10*inch))
    blade_details.fuller_right = Line(blade_details.fuller.end, (1.5*inch, 10*inch))
    blade_details.AddConstraint(Vertical(blade_details.fuller))
    
    blade.AddConstraint(Coincident(blade_details.fuller_left.end, blade.center.top_left))
    blade.AddConstraint(Coincident(blade_details.fuller_right.end, blade.center.top_right))

    # Hilt Geometry

    # Cross_Gaurd
    cross_guard.crosspiece = Rectangle((0,0), 5*inch, 1*inch)
    cross_guard.left_cap = Arc(cross_guard.crosspiece.left.midpoint, cross_guard.crosspiece.top_left, cross_guard.crosspiece.bottom_left)
    cross_guard.right_cap = Arc(cross_guard.crosspiece.right.midpoint, cross_guard.crosspiece.bottom_right, cross_guard.crosspiece.top_right)
    cross_guard.AddConstraint(HorizontallySymmetric(cross_guard.right_cap.center, cross_guard.left_cap.center))
    
    # Grip
    grip.handle = Rectangle((0,0), 1.5*inches, 4*inches)

    # Pommel
    pommel.blank = Circle((0,0), 1*inches)

    jewel_setting.hole = Circle((0,0), .5*inches)

    hilt.AddConstraint(Coincident(grip.handle.bottom_left, pommel.blank))
    hilt.AddConstraint(Coincident(grip.handle.bottom_right, pommel.blank))

    return sword


with AIDL_2D:
    model = toy_sword()
print(model.Solve())
model.Render()

```



Writes an AIDL program to generate a wrench

```python
from AIDL import *

def wrench(head_width = 5*mm):
    """
    Creates a combination wrench (crescent and box-end)
    for hex-bolts wit the given hexagonal head width (flat-to-flat)
    """

    wrench = Solid()
    # Compute the corner-to-corner radius of the hex-head, then add 5% tollerance
    box_end_radius = (head_width / sqrt(3)) * 1.05

    # Add the parameter to th the wrench so it can be computed by the solver
    wrench.box_end_radius = box_end_radius
    wrench.shaft = Rectangle((0,0), 6*head_width, head_width)
    
    crescent_head = Solid(shift=(-3.5*head_width, 0))
    crescent_head.disk = Circle((0,0), head_width)
    crescent_profile = Hole()
    crescent_head.profile = crescent_profile

    # Cut out a the crescent jaws. They consist of a rectangular slot with a curved back end. A second
    # rectangle blunts the whole crescent by cutting off the end of the disk
    crescent_profile.jaws = Rectangle((-head_width,0), 2*head_width, head_width)
    crescent_profile.curve = Arc(crescent_profile.jaws.right.midpoint, crescent_profile.jaws.right.start, crescent_profile.jaws.right.end )
    crescent_profile.blunting = Rectangle((-head_width,0), head_width/2, 2*head_width)

    # Make the box end. This is a circular head with a circular, toothed hole
    box_end_head = Solid(shift=(3*head_width + box_end_radius, 0))
    box_end_head.disk = Circle((0,0), 1.5*box_end_radius)
    box_end_profile = Hole()
    box_end_head.profile = box_end_profile

    box_end_profile.socket = Circle((0,0), box_end_radius)
    socket_teeth = Hole() # the teeth are a hole in the socket hole, and so positive at the level of the wrench
    box_end_profile.teeth = socket_teeth
    n_teeth = 12
    tooth_radius = .1 * box_end_radius # with 12 teeth there is about a 6% tollerance, add to it the 5% we already have (11%), then back off to still have some wiggle room
    socket_teeth.teeth = [Circle(
        (box_end_radius*cos(i*2*pi/n_teeth), box_end_radius*sin(i*2*pi/n_teeth)), tooth_radius) for i in range(n_teeth)]

    wrench.crescent_head = crescent_head
    wrench.box_end_head = box_end_head

    return wrench


with AIDL_2D:
    model = wrench()
print(model.Solve())
model.Render()

```

