Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Example: bike_pack_mount

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;
use std::math::*;

sketch Base(
    width = 130.0mm,
    height = 23.5mm,
    radius = 3mm,
) {
    RoundedRect(width, height, radius);
}

part RackRails(
    rack_rail_distance = 100mm,
    rack_rail_diameter = 10.5mm,
) {
    {
        rd = (rack_rail_distance + rack_rail_diameter) / 2; 
        Circle(d = rack_rail_diameter, c = (x = [-0.5,0.5]mm, y = 0mm))
            .hull()
            .translate(x = [-rd, rd]);
    }
    .extrude(40mm)
    .orient(Y)
    .translate(y = -15mm);
}

part Mount(
    height: Length,
    screw_distance = 94mm,
    screw_hole_diameter = 6mm,
    rack_rail_distance = 100mm,
    rack_rail_diameter = 10.5mm,
) {
    d = screw_distance / 2;
    screws = Circle(d = screw_hole_diameter, c = (x = [-d,d], y = 0.0mm));

    Base().extrude(height) - screws.extrude(height + 10mm) - RackRails();
}

pub part Lower() {
    Mount(height = 15mm);
}

pub part Upper() {
    Mount(height = 55mm);
}

#[export = "lower.stl"]
Lower().translate(y = 50mm);

#[export = "upper.stl"]
Upper().translate(y = -50mm);

2D Output : None

3D Output : None

Example: bricks

Module: brick

Report

// file: brick.µcad
// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;
use std::math::*;

pub const SPACING = 8mm;
pub const THICKNESS = 1.2mm;
pub const BASE_HEIGHT = 9.6mm;
pub const TOLERANCE = 0.2mm;

pub part Brick(rows = 2, columns = 4, base_height = BASE_HEIGHT) {
    width = columns * SPACING - TOLERANCE;
    height = rows * SPACING - TOLERANCE;

    base = {
        Frame(width, height, THICKNESS);
        r = rows - 1;
        c = columns - 1;
        n_rings = r * c;
        if n_rings > 0 {
            Ring(outer_diameter = 6.51mm, inner_diameter = 4.8mm)
                .multiply(n_rings)
                .distribute_grid(
                    width = width - width / columns,
                    height = height - height / rows,
                    rows = r, 
                    columns = c
                );
        }
    }
    .extrude(base_height - THICKNESS);

    cap = Rect(width, height).extrude(THICKNESS);

    knobs = Circle(d = 4.8mm)
        .multiply(rows * columns)
        .distribute_grid(width, height, rows, columns)
        .extrude(1.7mm);

    { base; cap; knobs; }.align(Z).union();
}

n = 4;

Brick(
    rows = [1..n], 
    columns = [1..n], 
    base_height = BASE_HEIGHT * [1 / 3, 100%, 200%, 300%]
)
.distribute_grid(cell_size = n * 10mm, 
    rows = 2*n, 
    columns = 2*n,
);

2D Output : None

3D Output : None

Module: tutorial

Report

// file: tutorial.µcad
// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;

pub const SPACING = 8mm;

op grid(columns: Integer, rows: Integer) {
    @input
        .translate(x = [1..columns] * SPACING, y = [1..rows] * SPACING)
        .center()
}

sketch Base(
    columns: Integer,
    rows: Integer,
    width: Length,
    height: Length
) {
    thickness = 1.2mm;
    frame = Frame(width, height, thickness);
    struts = Ring(outer_diameter = 6.51mm, inner_diameter = 4.8mm)
        .grid(columns = columns-1, rows = rows-1);
    frame | struts;
}

use Rect as Cap;

sketch Knobs(columns: Integer, rows: Integer) {
    Circle(d = 4.8mm)
        .grid(columns, rows);
}

pub part LegoBrick(rows = 2, columns = 4, base_height = 9.6mm) {
    width = columns * SPACING - 0.2mm;
    height =rows * SPACING - 0.2mm;
    cap_thickness = 1.0mm;

    base = Base(rows, columns, width, height)
        .extrude(base_height - cap_thickness);

    cap = Cap(width, height)
        .extrude(cap_thickness)
        .translate(z = base_height - cap_thickness);

    knobs = Knobs(rows, columns)
        .extrude(1.7mm)
        .translate(z = base_height);

    base | cap | knobs;
}

// render a brick with default values
LegoBrick();

2D Output : None

3D Output : None

Module: use_bricks

Report

// file: use_bricks.µcad
// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

mod brick;

use brick::*;

// 2x2 double height
double_2x2 = Brick(rows = 2, columns = 2, base_height = 9.6mm * 2);

// 4x2 single height
single_4x2 = Brick(rows = 4, columns = 2);

// 3x2 one-third height
third_3x2 = Brick(rows = 3, columns = 2, base_height = 3.2mm);

// generate geometry placing all elements side by side
use std::ops::translate;

single_4x2;
double_2x2.translate(y = -40mm);
third_3x2.translate(y = 40mm);

2D Output : None

3D Output : None

Example: buffer_stand

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::ops::*;
use std::geo2d::*;
use std::math::*;

part BufferStand() {
    size = 5.25in;
    width = 5.875in;

    base = (Circle(d = width) & Rect(width, height = size / 2))
        .extrude(4.0in);

    side = {
        Line(height = size/2);
        Circle(d = 1.5in).translate(x = -3.25in);
    }
    .hull()
    .extrude(width)
    .orient(X)
    .translate(x = -width / 2);

    front_cut_off = {
        w = 4.25in;
        d = w * 0.5 - 0.5in;

        Circle(r = 0.5in, c = (x = [d,-d], y = 0in));
        Line(width = w, p = (x = -w * 0.5, y = -4.0in));
    }
    .hull()
    .extrude(width)
    .orient(Y)
    .translate(y = -width / 4, z = 0.75in);

    bearing = (Rect(width = 1.25in, height = width - 1in) | Rect(height = width, width = 0.5in))
        .revolve()
        .orient(X)
        .translate(z = 3.25in);

    mount = Rect(height = 4.25in - 3.5in, width = 1.5in * 50%, x = 0mm, y = 0mm)
        .revolve()
        .orient(X)
        .translate(x = [2.0, -2.0]in, z = 3.25in);
    
    bottom_bar = {
        Circle(d = 0.15in).translate(x = -1.35in);
        Line(height = 0.5in);
    }
    .hull()
    .extrude(width)
    .orient(X)
    .translate(x = -width / 2);

    base & (side - front_cut_off) | (base & bottom_bar) | mount - bearing;
}

BufferStand();

2D Output : None

3D Output : None

Example: chess

Report

// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later


use std::geo2d::*;
use std::geo3d::*;
use std::ops::*;
use std::math::*;

// https://de.wikipedia.org/wiki/Bauhaus-Schachspiel

pub TILE_SIZE = 5.8cm;
const BASE_SIZE = TILE_SIZE * 70%;


pub part Pawn() {
    Cube(BASE_SIZE * 80%)
}

pub part King() {
    { 
        Cube(BASE_SIZE);
        Cube(BASE_SIZE / sqrt(2)).rotate(45°);
    }.align(Z)
}

pub part Queen() {
    { 
        Cube(BASE_SIZE);
        Sphere(diameter = BASE_SIZE / sqrt(2));
    }.align(Z)
}

pub part Bishop() {
    size = BASE_SIZE / 3;
    Rect(size * 90%)
        .translate(size * 150%, [45,-135]°)
        .hull()
        .mirror(X)
        .union()
        .extrude(BASE_SIZE)
        .rotate(x = 90°)
        .center()
}

pub part Rook() {
    Cube(BASE_SIZE)
}

pub part Knight() {
    {
        height = BASE_SIZE/2;
        r = Rect(BASE_SIZE);
        (r - r.translate(x = height, y = height)).extrude(height);
    }.rotate([0,90]°).align(Z)
}


{
    Rook();
    Knight();
    Bishop();
    Queen();
    King();
    Bishop();
    Knight().rotate(x = 180°).rotate(90°);
    Rook();
    8 * Pawn();
}
.distribute_grid(cell_size = TILE_SIZE, rows = 2, columns = 8)
.translate(y = TILE_SIZE * 3)
.rotate([0, 180]°);

board_height = 1mm;
Rect(TILE_SIZE * 8).extrude(board_height).translate(z = -board_height);

2D Output : None

3D Output : None

Example: csg_cube

Report

// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::math::*;
use std::ops::*;
use std::geo3d::*;

part CsgCube(size: Length) {
    s = size / sqrt(2.1);
    body = Sphere(radius = s) & Cube(size);
    holes = Cylinder(size, diameter = s).orient([X,Y,Z]);
    body - holes;
}

CsgCube(50mm);

2D Output : None

3D Output : None

Example: cylinder_stack

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo3d::*;
use std::debug::*;

part CylinderStack(heights: [Length], radii: [Length], offset: Length = 0mm) {
    assert(heights.count() == radii.count() - 1, "radii must be an array with one more elements that heights");

	self = Cylinder(height = heights.head(), radius_bottom = radii.head(), radius_top = radii.tail().head(), offset);
	if heights.count() > 1 {
		self | CylinderStack(heights = heights.tail(), radii = radii.tail(), offset = offset + heights.head())
	} else {
		self
	}
}

CylinderStack(heights = [5, 15, 5]mm, radii = [6, 4, 4, 6]mm);


2D Output : None

3D Output : None

Example: dome

Report

// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later
// file: dome.µcad

use std::geo2d::*;
use std::ops::*;

pub part Dome(size: Length, strut_width = 1mm) {
	Rect(strut_width)
		.translate(x = size)
		.revolve(180°)
		.rotate(x = -[0..6]*180°/6)
		.rotate(z = [0..3]*180°/3);
}

Dome(10mm, strut_width = 0.25mm);


2D Output : None

3D Output : None

Example: drill_plate

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;

/// A drill plate with four holes.
#[color = "red"]
sketch DrillPlate(size = 100mm, hole_size = 10mm) {
    l = size/2 - hole_size;
    Rect(size) - Circle(diameter = hole_size).translate(x = [-l,l], y = [-l,l])
}

DrillPlate();

2D Output : None

3D Output : None

Example: footpad

Report

// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::ops::*;
use std::geo2d::*;
use std::math::*;

part FootPad(
    height = 45mm,
    base = (x = 155mm, y = 101mm, z = 18mm),
    slot = (x = 8mm, y = 13mm),
    center_hole = (d_inner = 42mm, d_outer = 55mm),
    corner_hole = (d_lower = 14mm, d_upper = 22mm),
    corner_radius = 15mm,
) {
    s = (x = base.x / 2 - corner_radius, y = base.y / 2 - corner_radius);
    hole_pos = (x = [-1,1]*s.x, y = [-1,1]*s.y);
    radius = corner_radius;
    
    base_lower = Circle(radius, c = hole_pos)
        .hull()
        .extrude(12mm);

    base_upper_height = slot.x;
    base_lower_height = base.z - base_upper_height;

    base_upper = {
        top_left = (x = -s.x, y = s.y);
        top_right = (x = s.x, y = s.y);
        bottom_left = (x = -s.x, y = -s.y);
        bottom_right = (x = s.x, y = -s.y);
        (Circle(radius, c = (x = top_left.x, y = bottom_right.y)).hull() 
        | Circle(radius, c = (x = top_right.x, y = bottom_left.y)).hull());
    }
    .extrude(base_upper_height)
    .translate(z = base_lower_height);
    
    center = {
        (Circle(d = center_hole.d_outer) - Circle(d = center_hole.d_inner))
            .extrude(height);
        
        Rect(height = slot.x, width = center_hole.d_outer)
            .extrude(slot.y)
            .translate(z = height - slot.y);
        
    }.subtract();

    holes = {
        Circle(d = corner_hole.d_lower, c = hole_pos)
            .extrude(height = base_lower_height);
        Circle(d = corner_hole.d_upper, c = hole_pos)
            .extrude(height = base.z * 2)
            .translate(z = base_lower_height);
        Circle(d = center_hole.d_inner)
            .extrude(height);
    }.union();

    base_lower | base_upper | center - holes;
}

FootPad();

2D Output : None

3D Output : None

Example: gears

Module: gear_2d

Report

// file: gear_2d.µcad
// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::ops::*;
use std::geo2d::*;

InvoluteGearProfile(1.0mm, 100);

2D Output : None

3D Output : None

Module: gears_3d

Report

// file: gears_3d.µcad
// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::ops::*;
use std::geo2d::*;
use std::math::*;

const TEETH = 20;

angle = 30° * TEETH;

part Gear(teeth: Integer) {
    gear_profile = InvoluteGearProfile(module = 1.0mm, teeth);

    prop pitch_radius = gear_profile.pitch_radius;
    prop outer_radius = gear_profile.outer_radius;

    n_holes = 5;

    bearing_hole = Circle(radius = gear_profile.pitch_radius / 8);
    
    hole_radius = pitch_radius / n_holes;
    holes = Circle(radius = pitch_radius / n_holes)
                .translate(x = outer_radius - hole_radius * 2)
                .rotate([1..n_holes] * 360° / n_holes);

    inner = Circle(radius = pitch_radius - 3mm);

    bearing = (Circle(radius = pitch_radius / 4) - bearing_hole).extrude(8mm) 
            - Circle(radius = gear_profile.module)
                .extrude(pitch_radius / 4)
                .translate(z = -pitch_radius / 4)
                .rotate(x = 90°)
                .translate(z = 5mm);

    bearing 
    | (inner - holes - Circle(radius = pitch_radius / 4)).extrude(2mm) 
    | (gear_profile - inner).extrude(4mm);
}


gear1 = Gear(80);
gear2 = Gear(40);
gear3 = Gear(20);

#[color = std::color::BLUE]
#[export = "gear1.stl"]
gear1.rotate(angle / gear1.teeth);

#[color = std::color::RED]
#[export = "gear2.stl"]
gear2
    .rotate(-angle / gear2.teeth + 360°/gear2.teeth/2.0)
    .translate(gear2.outer_radius + gear1.pitch_radius, 135°);

#[color = std::color::GREEN]
#[export = "gear3.stl"]
gear3
    .rotate(-angle / gear3.teeth)
    .translate(gear1.outer_radius + gear3.pitch_radius, 45°);


2D Output : None

3D Output : None

Module: herringbone_gears

Report

// file: herringbone_gears.µcad
// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::ops::*;
use std::geo2d::*;
use std::math::*;

part Gear(teeth: Integer)  {
    gear_profile = InvoluteGearProfile(1.5mm, teeth);
    prop outer_radius = gear_profile.outer_radius;
    prop pitch_radius = gear_profile.pitch_radius;
    prop tooth_angle = gear_profile.tooth_angle;
    prop tooth_distance = gear_profile.tooth_distance;
    prop module = gear_profile.module;

    disc = Circle(radius = pitch_radius - module * 400%);
    web = {
        sector = Sector(disc.radius, 360° / 5)
            .buffer(-disc.radius * 15%)
            .buffer(disc.radius * 12%);
        holes = (sector - Circle(radius = module * 1000%))
            .rotate([0..4] * 360° / 5);
        hub = InvoluteGearProfile(2.5mm, 7);
        disc - holes - hub;
    }.extrude(tooth_distance * 300%).center();

    rim = (gear_profile - disc)
        .extrude(tooth_distance * 200%, tooth_angle)
        .mirror(-Z);

    web | rim;
}

gear1 = Gear(90);
gear2 = Gear(60);

gear1.reflect(X);
gear2
    .rotate(gear2.tooth_angle * 50%)
    .translate(gear1.outer_radius + gear2.pitch_radius, [1..3] * 360° / 3);



2D Output : None

3D Output : None

Example: hole_test

Report

// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::geo3d::*;
use std::ops::*;
use std::math::*;

const THICKNESS = 2mm;

part Hole(diameter: Length) {
    r = diameter * 50%;
    Cylinder(radius_bottom = r, radius_top = r + THICKNESS * 50%, height = THICKNESS);
}

r = RoundedRect(60mm, radius = 10mm).extrude(THICKNESS);

holes = Hole([1..9] * 0.25mm)
    .distribute_grid(size = 60mm, rows = 3, columns = 3);

r - holes;

2D Output : None

3D Output : None

Example: letter_cube

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::math::*;
use std::ops::*;
use std::geo3d::*;
use std::geo2d::*;

part Letter(letter: String, size: Length, axis: Vec3) {
    Text(letter, height = size)
        .center()
        .contour(thickness = 0.25mm, distance = 0.5mm)
        .extrude(1mm)
        .translate(z = size / 2 - 0.5mm)
        .orient(axis);
}


part LetterCube(size: Length) {
    s = size / sqrt(2.1);
    body = Cube(size) & Sphere(r = s);
    p = Letter("P", size, [-Z, Z]).rotate(z = -90°);
    t = Letter("T", size, [-X, X]).rotate(x = 90°);
    f = Letter("F", size, [-Y, Y]);
    body - (p | t | f);
}

LetterCube(20mm).translate(z = 10mm);

2D Output : None

3D Output : None

Example: lid

Report

// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

/// A part called `Lid` with three parameters.
part Lid(
    thickness = 1.6mm,
    inner_diameter = 16.0cm,
    height = 20.0mm,
) {
    // Calculate the outer diameter
    outer_diameter = 2.0 * thickness + inner_diameter;

    // Create two cylinders, one for the outer and one for the inner
    outer = std::geo3d::Cylinder(d = outer_diameter, h = height);
    inner = std::geo3d::Cylinder(d = inner_diameter, h = height).std::ops::translate(z = thickness);

    // Calculate the difference between two translated cylinders and output them
    outer - inner;
}


// `l` is the instance of the lid model
l = Lid();

l; // Instantiate the lid.

2D Output : None

3D Output : None

Example: logo

Report

2D Output : None

3D Output : None

Example: multi_export

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::ops::*;

#[export = "rect.svg"]
std::geo2d::Rect(42mm)
    .rotate(45°);

#[export = "circle.svg"]  // Will be exported to `circle.svg`
std::geo2d::Circle(r = 42mm)
    .translate(x = 42mm);

2D Output : None

3D Output : None

Example: ngons

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;

(Hexagon(20mm) - Ngon(5, 10mm)).extrude(20mm);

2D Output : None

3D Output : None

Example: spirograph

Report

// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;

mod logo;

use logo::microcad::Logo;

const TEETH_SIZE = 0.5mm;
const THICKNESS = 2mm;
const BORDER_WIDTH = 7mm;

sketch GearProfile(teeth: Integer) {
    prop module = TEETH_SIZE;
    prop radius = teeth * module;
    SinusoidalGearProfile(module, teeth, stretch = 0.6, roundness = 0.6);
}


sketch RingProfile(outer_teeth: Integer, inner_teeth: Integer, border_width = BORDER_WIDTH) {
    inner_gear = GearProfile(inner_teeth);
    outer_gear = GearProfile(outer_teeth);
    prop radius = outer_gear.radius;
    prop shift = inner_gear.radius * 40%;

    sector = {
        disc = Circle(r = outer_gear.radius - border_width);
        sector = Sector(r = outer_gear.radius, start = 30°, end = 90°).rotate(60°);
        inner_disc = Circle(r = inner_gear.radius - border_width * 250%);
        ring = Ring(o_r = inner_gear.radius + border_width - TEETH_SIZE * 2, thickness = border_width);

        (disc - (sector - inner_disc) - ring.translate(x = shift))
            .buffer(-TEETH_SIZE * 2)
            .buffer(TEETH_SIZE * 2);
    };

    outer_gear - sector - inner_gear.translate(x = shift);
}

part PencilHole(pencil_hole_diameter = 0.5mm, offset = 0.0mm) {
    Circle(d = pencil_hole_diameter)
        .extrude(THICKNESS, scale = (x = 300%, y = 300%))
        .translate(y = offset);
}

part PencilHoles(radius: Length, n = 4, start = 4, angle = 22.5°) {
    {
        PencilHole(offset = [start..n*2-1] * radius / n / 2);
        PencilHole(offset = ([start..n*2-1] + 0.5) * radius / n / 2).rotate(-angle);
        PencilHole(offset = ([start..n*2-1] + 1/3) * radius / n / 2).rotate(angle);
    }.rotate(90°)
}

#[export = "spirograph_frame.stl"]
#[color = std::color::BLUE]
{
    outer_gear = GearProfile(180);
    size = outer_gear.radius * 200% + 3 * BORDER_WIDTH;
    frame = {
        border = RoundedRect(size, radius = BORDER_WIDTH * 200%);    
        border - outer_gear;
    }.extrude(THICKNESS*2);

    logo2 = Logo(size = BORDER_WIDTH*85%)
        .extrude(THICKNESS)
        .translate(x = size * 35%, y = size * 43%, z = THICKNESS * 150%);
    
    url = Text("microcad.xyz", BORDER_WIDTH*150%)
        .center()
        .extrude(THICKNESS)
        .translate(x = size * 32%, y = -size * 46%, z = THICKNESS * 150%);

    frame - logo2 - url;
}.translate(x = 0mm);

ring_1 = RingProfile(outer_teeth = 180, inner_teeth = 120);
ring_2 = RingProfile(outer_teeth = 120, inner_teeth = 72);
ring_3 = RingProfile(outer_teeth = 72, inner_teeth = 36);



#[export = "spirograph_ring_1.stl"]
#[color = std::color::GREEN]
{
    ring = ring_1.extrude(THICKNESS);
    pencil_holes = PencilHoles(ring_1.radius, n = 6, start = 6);
    ring - pencil_holes;
}.translate();

#[export = "spirograph_ring_2.stl"]
#[color = std::color::RED]
{
    ring = ring_2.extrude(THICKNESS);
    pencil_holes = PencilHoles(ring_2.radius, n = 5, start = 5);
    ring - pencil_holes;
}.translate(x = ring_1.shift);


#[export = "spirograph_ring_3.stl"]
#[color = std::color::YELLOW]
{
    ring = ring_3.extrude(THICKNESS);
    pencil_holes = PencilHoles(ring_3.radius);
    ring - pencil_holes;
}.translate(x = ring_2.shift + ring_1.shift);

gear = {
    profile = GearProfile(36);
    gear = profile.extrude(THICKNESS);
    pencil_holes = PencilHoles(profile.radius, start = 1, n = 2, 120°);

    gear - pencil_holes;
};

#[export = "spirograph_gear.stl"]
#[color = std::color::TEAL]
gear.translate(x = ring_1.shift + ring_2.shift + ring_3.shift);

2D Output : None

3D Output : None

Example: text

Module: love

Report

// file: love.µcad
// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;

sketch Heart(size: Length) {
    r = size / 3;
    Circle(d = size).translate(x = [r, -r], y = r) | Rect(size).rotate(45°);
}

#[color = std::color::GREEN]
Text("µcad", height = 80mm).center().translate(x = -110mm).extrude(4mm);

#[color = std::color::RED]
Heart(40mm).extrude(12mm);

#[color = std::color::BLUE]
Text("PTF", height = 80mm).center().translate(x = 90mm).extrude(4mm);

2D Output : None

3D Output : None

Module: text_plate

Report

// file: text_plate.µcad
// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::geo2d::*;
use std::ops::*;
use std::math::*;

sketch TextPlate(text: String, height: Length = 30mm) {
    letters = Text(text, height).center();
    plate = RoundedRect(60mm, radius = 20mm);
    plate - letters
}

TextPlate("Hello µcad", 10mm)
    .extrude(2mm, 0°);

2D Output : None

3D Output : None

Example: torus

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

std::geo3d::Torus([1..5] * 10mm, minor_radius = 3mm);

2D Output : None

3D Output : None

Example: use_dome

Report

// Copyright © 2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

mod dome;

dome::Dome(10mm, strut_width = 0.5mm).std::ops::mirror(std::math::Z);

2D Output : None

3D Output : None