Skip to content

3. Computer Aided design

I explored creating a parametric capybara model using AI assistance. The goal was to see if I could use natural language prompts to generate CAD scripts, similar to how I use Cursor for software development.

1. Raster Image Example

I used AI to generate reference images and diagrams for the capybara model.

Prompt used:

“generate the shape of a capybara that is easy to design in cad and add color”

Capybara Render Capybara Technical Diagram

2. SVG Example

I also generated a vector logo for the project.

Prompt used:

“generate a minimalist vector logo for a capybara project, clean lines, SVG format”

Capybara Logo

3. OpenSCAD Example

For my software work, I am heavily spoiled by AI where I would use tools like Cursor to write all the code and I would just need to review the code to ensure the AI didn’t hallucinate. I wanted to try the same thing with OpenSCAD and ChatGPT prompts.

Prompt used:

“Write an OpenSCAD script for a parametric capybara. Use simple primitives like ellipsoids and cylinders. Make it look like a toy with a warm brown color.”

OpenSCAD Model

// Parametric Simple Capybara (OpenSCAD)
// - Designed for easy CAD-style tweaking and 3D printing
// - Uses rounded capsules/ellipsoids + legs as rounded cylinders
// Units: mm

$fn = 64; // smoothness (lower for faster preview)

// -------------------------
// Parameters
// -------------------------
body_len   = 160;
body_wid   = 75;
body_ht    = 85;

head_len   = 60;
head_wid   = 52;
head_ht    = 52;

leg_rad    = 11;
leg_ht     = 30;

stance_y   = 26;   // how far legs are from centerline
front_leg_x= 35;   // from body front
rear_leg_x = 125;  // from body front

belly_clear= 25;   // lifts body above ground

ear_rad    = 10;
ear_ht     = 8;

eye_rad    = 6;

// Colors (simple + toy-like)
body_col   = [0.63, 0.41, 0.22]; // warm brown
hoof_col   = [0.25, 0.17, 0.10]; // darker feet
eye_col    = [0.05, 0.05, 0.05]; // near-black

// -------------------------
// Helpers
// -------------------------

// Ellipsoid via scaled sphere
module ellipsoid(l, w, h) {
  scale([l/2, w/2, h/2]) sphere(r=1);
}

// Rounded cylinder (a "capsule" pillar) for legs
module capsule_cyl(r=10, h=30) {
  // cylinder + two spheres gives a soft toy-leg silhouette
  union() {
    translate([0,0,r]) cylinder(r=r, h=max(0, h - 2*r));
    translate([0,0,r]) sphere(r=r);
    translate([0,0,h-r]) sphere(r=r);
  }
}

// Slightly flattened ear nub
module ear() {
  scale([1.0, 0.8, 0.6]) sphere(r=ear_rad);
}

// -------------------------
// Capybara
// -------------------------
module capybara() {
  // Define body coordinate frame:
  // Body "front" at x=0, back at x=body_len.
  // Centered in Y, sits above ground by belly_clear.

  body_center = [body_len/2, 0, belly_clear + body_ht/2];

  // --- Body + head (main mass) ---
  color(body_col)
  union() {
    // Body: ellipsoid, slightly stretched
    translate(body_center)
      ellipsoid(body_len, body_wid, body_ht);

    // Head: ellipsoid, placed forward and slightly higher
    // Attach near body front
    translate([-head_len*0.45, 0, belly_clear + body_ht*0.55])
      rotate([0,0,0])
      ellipsoid(head_len, head_wid, head_ht);

    // Neck blend: smaller ellipsoid to smooth head-body junction
    translate([body_len*0.10, 0, belly_clear + body_ht*0.55])
      ellipsoid(body_len*0.35, body_wid*0.75, body_ht*0.75);

    // Ears (simple nubs)
    translate([-head_len*0.25,  head_wid*0.25, belly_clear + body_ht*0.95])
      ear();
    translate([-head_len*0.25, -head_wid*0.25, belly_clear + body_ht*0.95])
      ear();
  }

  // --- Legs (4) ---
  // Put legs under the body, then color feet tips darker.
  // (We model whole leg in body color, then add a "hoof cap".)
  for (xPos = [front_leg_x, rear_leg_x]) {
    for (yPos = [stance_y, -stance_y]) {
      // leg
      color(body_col)
      translate([xPos, yPos, 0])
        capsule_cyl(r=leg_rad, h=leg_ht);

      // darker foot cap
      color(hoof_col)
      translate([xPos, yPos, 0])
        cylinder(r=leg_rad*1.02, h=leg_ht*0.25);
    }
  }

  // --- Eyes (simple spheres) ---
  // Place one eye on visible side; duplicate if you want both.
  color(eye_col) {
    translate([-head_len*0.25, head_wid*0.42, belly_clear + body_ht*0.70])
      sphere(r=eye_rad);
    translate([-head_len*0.25, -head_wid*0.42, belly_clear + body_ht*0.70])
      sphere(r=eye_rad);
  }

  // --- Nose dots (optional) ---
  color(eye_col) {
    translate([-head_len*0.55,  head_wid*0.12, belly_clear + body_ht*0.62])
      sphere(r=2.0);
    translate([-head_len*0.55, -head_wid*0.12, belly_clear + body_ht*0.62])
      sphere(r=2.0);
  }
}

// Render
capybara();

4. build123d Example

I also used the Python library build123d to create a more refined parametric model based on the technical diagram.

Prompt used: Detailed Cursor Prompt

build123d Screenshot 1 build123d Screenshot 2 build123d Screenshot 3

#!/usr/bin/env python3
"""
Parametric Capybara model using build123d
Refined to match technical diagram: bf22da9c-4340-47e1-a050-bdc506f55deb.png
"""

try:
    from build123d import *
    from ocp_vscode import show, show_object, set_port
    import math

    print("✓ build123d is available!")
    set_port(3939)

    # --- Dimensions from Diagram ---
    TOTAL_LENGTH = 140
    WIDTH = 72          # Max Width
    HEIGHT = 78         # Max Height

    # Side View Keypoints (X, Z_bottom, Z_top)
    # Nose starts at X=0, Z_bottom=24.
    # Ear plane at X=34. Height at ear plane is ~72.
    # Max height at X ~ 80-90 is 78.

    # Leg Dimensions
    LEG_INNER_GAP_X = 80
    LEG_INNER_GAP_Y = 26

    # From front view: Total width 72. Leg inner gap 26.
    # Leg Width = (72 - 26) / 2 = 23mm.
    LEG_WIDTH = 23
    LEG_DEPTH = 23 # Assume roughly square/circular legs

    # Leg Center Y
    LEG_CENTER_Y = (LEG_INNER_GAP_Y + LEG_WIDTH) / 2  # (26 + 23)/2 = 24.5

    with BuildPart() as capybara:

        # --- Body ---
        # Profiles for loft - "loaf" shape
        # Profile Data: (X, Width, Height_Bottom, Height_Top)
        # Note: Z=0 is ground.
        profiles = [
            (0,   18, 24, 42),   # Nose Tip (Z_bot=24 from diagram)
            (15,  45, 22, 58),   # Snout
            (34,  72, 20, 72),   # At Ears (Width 72, Height 72 from diagram)
            (70,  72, 18, 78),   # Mid Body (Max Height 78)
            (110, 68, 18, 70),   # Hips
            (140, 20, 24, 45),   # Tail End
        ]

        sections = []
        for x, w, z_bot, z_top in profiles:
            h = z_top - z_bot
            z_center = (z_top + z_bot) / 2

            with BuildSketch(Plane.YZ.offset(x)) as sk:
                with Locations((0, z_center)):
                    # Squircle profile
                    RectangleRounded(w, h, radius=min(w, h) * 0.45)
            sections.append(sk.sketch)

        loft(sections)

        # --- Legs ---
        # Leg Profile: Rounded Square (Squircle)

        # Adjust X positions to fit within 140mm length visually
        leg_centers_x = [45, 115] # 45 to 115 is 70mm span.

        leg_locs = [
            (leg_centers_x[0], LEG_CENTER_Y),
            (leg_centers_x[0], -LEG_CENTER_Y),
            (leg_centers_x[1], LEG_CENTER_Y),
            (leg_centers_x[1], -LEG_CENTER_Y),
        ]

        for x, y in leg_locs:
            with BuildPart() as leg:
                with Locations((x, y, 0)):
                    # Leg shaft
                    Box(LEG_DEPTH, LEG_WIDTH, 40, align=(Align.CENTER, Align.CENTER, Align.MIN))

                # Round the legs (vertical edges) to match body style
                fillet(leg.edges().filter_by(Axis.Z), radius=8)

                # Round the feet (bottom edges)
                foot_edges = leg.edges().filter_by(lambda e: e.center().Z < 0.1)
                fillet(foot_edges, radius=4)

            add(leg.part)

        # --- Ears ---
        ear_x = 34
        ear_z = 70 # Slightly embedded
        ear_y = 25 # Wide apart

        with Locations((ear_x, ear_y, ear_z), (ear_x, -ear_y, ear_z)):
            with BuildPart() as ear:
                Sphere(6)
            add(ear.part)

        # --- Eyes ---
        eye_x = 22
        eye_y = 28
        eye_z = 55

        with Locations((eye_x, eye_y, eye_z), (eye_x, -eye_y, eye_z)):
            Sphere(3.5)

    # --- Export ---
    if capybara.part:
        export_stl(capybara.part, "capybara.stl")
        export_step(capybara.part, "capybara.step")