Metadata-Version: 2.4
Name: codetocad
Version: 2026.7.21.1
Summary: One language to define your mechanical and electrical CAD designs, federated to your modeling application.
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: numpy>=2.5.1
Requires-Dist: build123d
Requires-Dist: open3d>=0.19.0
Requires-Dist: mujoco
Requires-Dist: nicegui>=3
Requires-Dist: matplotlib
Requires-Dist: pillow
Requires-Dist: skidl>=2.2.3
Requires-Dist: pyserial
Requires-Dist: mpremote
Requires-Dist: paho-mqtt
Requires-Dist: rerun-sdk
Requires-Dist: playwright
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Provides-Extra: pybullet
Requires-Dist: pybullet; extra == "pybullet"
Provides-Extra: calculix
Requires-Dist: pygccx; extra == "calculix"
Requires-Dist: matplotlib; extra == "calculix"
Provides-Extra: vesc
Requires-Dist: pyvesc; extra == "vesc"
Requires-Dist: pyserial; extra == "vesc"

# CodeToCAD

CodeToCAD accelerates mechanical and electrical CAD design, simulations/FEA,
controls software and MCU firmware by giving you **one language to define your
design** — your script is federated to the modeling or design application
automatically.

## Install

```sh
pip install codetocad
```

## Quick start (CLI)

```sh
codetocad init cup
```

This creates a `cup/` folder with a `cup.py` file and opens an interactive
menu to create parts and sketches, transform, boolean, shell, constrain and
export them. Every action updates generated python part files (for example
`cup_cylinder.py`), so the CLI extends to the full functionality of the
CodeToCAD classes.

Run a script:

```sh
codetocad path/to/script.py
```

## Quick start (Python)

```python
import codetocad

body = codetocad.cylinder(radius="2cm", height="5cm")
body.shell(thickness="5mm")
body.set_material(codetocad.aluminum_material())
body.export("cup.stl")
```

## Highlights

- **Units**: floats are meters/radians; strings such as `"2in"`, `"10 deg"`
  or expressions like `"2in - 5mm"` are parsed and converted.
- **Locations**: 6-dof positions/orientations; `CubeLocations` shortcuts to
  the 23 topological locations of any shape's bounding cube; the
  `@codetocad.location` decorator marks named locations on your part classes.
- **Parts & assemblies**: `Part2D`/`Part3D` with extrude, shell, fillet,
  chamfer, hole; `Assembly2D`/`Assembly3D` constraints (coincide, parallel,
  fixed, revolute, prismatic, ...) recorded in ledgers.
- **Primitives**: `cube`, `cylinder`, `sphere`, `rectangle`, `circle`,
  `text`, `import_file` and material presets.
- **ECAD**: `led`, `diode`, `resistor`, `capacitor`, `inductor`,
  `voltage_source`, `current_source` components (each a `Part3D` with pins, a
  value and a `Footprint`) wired into a `Circuit` of `Net`s — capture
  schematics with skidl and simulate with SPICE (below).
- **Mixins**: sensors (`CameraMixin`, `IMUMixin`, `MicrophoneMixin`) and
  actuators (`DCMotorMixin`, `BLDCMotorMixin`) for custom parts.
- **Fasteners**: `CommonFasteners` enum that can `build()` a part or apply
  features (clearance holes) to another part.

## Build123D integration

Install the extra (`uv sync --extra build123d`) and your parts are federated
to real OpenCascade solids — booleans, shells, fillets, chamfers, holes and
transforms are replayed natively, and geometry queries, analysis and STL/STEP
export use the native topology:

```python
from codetocad_integrations.build123d import make_cube

if __name__ == "__main__":
    cube = make_cube("10cm", "10cm", "5cm")
    cube.hole(cube.top_center, radius="4cm", amount="5cm")
    cube.export("my_cube.stl")
```

Subclass `codetocad_integrations.build123d.Part3D` and override
`build_native()` to model a custom base shape with the Build123D API; all
CodeToCAD operations still apply on top. `adapt(part)` converts any core
CodeToCAD part (including `led()`, `resistor()`, fasteners, ...) into a
Build123D-federated one.

<img src="codetocad_integrations/build123d/examples/images/gallery_vase.png" width="360">
<img src="codetocad_integrations/build123d/examples/images/gallery_handle.png" width="360">

See [codetocad_integrations/build123d/examples/](codetocad_integrations/build123d/examples/)
for the full gallery.

## Blender integration

With Blender on your PATH (or `CODETOCAD_BLENDER` pointing at it), the same
designs federate to Blender mesh objects — booleans, shells (solidify),
fillets/chamfers (bevel), holes and transforms are replayed with modifiers,
and you can export .stl, .obj, .glb, .fbx or a full .blend scene:

```python
from codetocad_integrations.blender import ensure_blender, make_cube

if __name__ == "__main__":
    ensure_blender()  # relaunches this script under `blender --background`
    cube = make_cube("10cm", "10cm", "5cm")
    cube.hole(cube.top_center, radius="4cm", amount="5cm")
    cube.export("my_cube.blend")
```

Subclass `codetocad_integrations.blender.Part3D` and override
`build_native()` to model with bpy/bmesh directly. See
[codetocad_integrations/blender/examples/](codetocad_integrations/blender/examples/).

<img src="codetocad_integrations/blender/examples/images/suzanne.png" width="360">
<img src="codetocad_integrations/blender/examples/images/shelled_cup.png" width="360">

## Simulation (PyBullet & MuJoCo)

Model in Build123D or Blender, assemble with joint constraints, and import
right into physics simulation — `simulate(part)` walks the assembly, exports
the meshes and generates a URDF (PyBullet) or MJCF (MuJoCo):

```python
from codetocad import Location
from codetocad_integrations.build123d import make_cube, make_cylinder
from codetocad_integrations.pybullet import simulate  # or ...mujoco

mount = make_cube("6cm", "6cm", "4cm", start_location=Location(z="52cm"))
rod = make_cylinder("1cm", "40cm", start_location=Location(z="30cm"))
pivot = Location.from_euler(0, 0, "50cm", x_deg=-90, name="pivot")
mount.revolute(pivot, rod, pivot)  # hinge about the Y axis

sim = simulate(mount, gui=True)
sim.set_joint_value("pivot", 1.0)
sim.run(10.0, realtime=True)
```

Joint axes come from the constraint Location's orientation, limits from
`min_limits`/`max_limits`, masses/inertias from part materials and geometry,
and `codetocad.Lighting` describes scene lights. Free-floating `scene_parts`
(objects the robot can interact with) and a `ground_plane` complete the
scene. See the examples in
[codetocad_integrations/pybullet/examples/](codetocad_integrations/pybullet/examples/)
and [codetocad_integrations/mujoco/examples/](codetocad_integrations/mujoco/examples/)
(a 6-DOF arm with a parallel-jaw gripper that picks up a cube, pendulum,
double pendulum).

<img src="codetocad_integrations/pybullet/examples/images/arm_6dof.gif" width="360">
<img src="codetocad_integrations/mujoco/examples/images/double_pendulum.gif" width="360">

## FEA (CalculiX)

Analyze the same parts with finite elements — `analyze(part)` meshes the
exported geometry with gmsh, applies fixtures/loads described with
Locations, solves with CalculiX via [pygccx](https://github.com/calculix/pygccx),
and returns displacement and von Mises stress fields with visualization:

```python
from codetocad import steel_material
from codetocad_integrations.build123d import make_cube
from codetocad_integrations.calculix import analyze

beam = make_cube("200mm", "20mm", "10mm")
beam.set_material(steel_material())

fea = analyze(beam)
fea.fix(beam.left_center)                          # clamp the left face
fea.add_force(beam.right_center, force=(0, 0, -100))
results = fea.solve()
print(results.max_displacement, results.max_von_mises)
results.visualize("beam_fea.png")
```

Materials carry elastic properties (`steel_material()`, `aluminum_material()`
or set `youngs_modulus`/`poissons_ratio` on any `MaterialBase`). The ccx
solver is auto-discovered from `CODETOCAD_CCX`, the PATH, or
`~/.codetocad/ccx/bin/ccx`. See
[codetocad_integrations/calculix/examples/](codetocad_integrations/calculix/examples/).

<img src="codetocad_integrations/calculix/examples/images/beam_fea.png" width="500">

## Visualization (Open3D)

Display any `Part3D` — core, Build123D- or Blender-federated — in an Open3D
window, or render a screenshot headlessly for docs/CI:

```python
from codetocad_integrations.build123d import make_cube
from codetocad_integrations.open3d import show, render

cube = make_cube("10cm", "10cm", "5cm")
cube.hole(cube.top_center, radius="4cm", amount="5cm")

show(cube)                          # interactive window
render(cube, path="cube.png")       # offscreen screenshot
```

The part is exported (`part.export()`) to a temporary mesh and loaded into
Open3D, so it works with any backend — Open3D itself isn't a CAD kernel. See
[codetocad_integrations/open3d/examples/](codetocad_integrations/open3d/examples/).

<img src="codetocad_integrations/open3d/examples/images/embossed_text_logo.png" width="500">

## ECAD: schematics (skidl) & simulation (SPICE)

Describe a circuit once as a `Circuit` of components and nets, then federate
it to schematic capture and circuit simulation — the same components are
`Part3D`s (each carries a `Footprint`), so they drop straight into a board
assembly:

```python
from codetocad import Circuit, resistor, voltage_source
from codetocad_integrations.skidl import export_netlist, export_schematic
from codetocad_integrations.spice import simulate

circuit = Circuit("divider")
v1 = circuit.add(voltage_source(dc=9))
r1, r2 = circuit.add(resistor("10k"), resistor("20k"))
circuit.connect(v1["+"], r1[1], name="VIN")
circuit.connect(r1[2], r2[1], name="VOUT")
circuit.connect(r2[2], v1["-"], circuit.gnd)

export_netlist(circuit, "divider.net")     # KiCad netlist (with footprints)
export_schematic(circuit, "divider.svg")   # schematic SVG (netlistsvg)

op = simulate(circuit).operating_point()
print(op.voltage("VOUT"))                   # 6.0
```

**Schematics (skidl).** `codetocad_integrations.skidl` converts a `Circuit`
into a [skidl](https://github.com/devbisme/skidl) circuit to run its ERC and
write KiCad netlists/XML, and renders schematic SVGs with standard analog
symbols via [netlistsvg](https://github.com/nturley/netlistsvg). Install with
`uv sync --extra skidl` plus `npm install -g netlistsvg`. See
[codetocad_integrations/skidl/examples/](codetocad_integrations/skidl/examples/).

<img src="codetocad_integrations/skidl/examples/images/voltage_divider.png" height="260">
<img src="codetocad_integrations/skidl/examples/images/led_board.png" height="260">
<img src="codetocad_integrations/skidl/examples/images/led_board_3d.png" height="200">

**Simulation (SPICE).** `codetocad_integrations.spice` builds a SPICE netlist
from the `Circuit` (diode/LED models are derived from each component's
electrical properties) and runs it with [ngspice](https://ngspice.sourceforge.io):
operating point, DC sweep, transient and AC analyses come back as numpy
vectors with `plot()`/`bode()` helpers. Install with `uv sync --extra spice`
plus ngspice (`brew install ngspice` / `apt install ngspice`). See
[codetocad_integrations/spice/examples/](codetocad_integrations/spice/examples/).

<img src="codetocad_integrations/spice/examples/images/rc_lowpass_bode.png" width="360">
<img src="codetocad_integrations/spice/examples/images/led_driver_current.png" width="360">

## WebApp control panels

Any `Part3D` can double as a sensor or actuator via mixins (`DCMotorMixin`,
`EncoderMixin`, `IMUMixin`, ...). Bind them to a `Microcontroller`'s pins and
a `PythonApp`/`WebApp` federates sliders, buttons, gauges and plots to the
same JSON-lines wire protocol the firmware speaks:

```python
from codetocad import Microcontroller, MicrocontrollerBoard, SerialCommunication, WebApp
from codetocad.mixins import DCMotorMixin, EncoderMixin

class GearMotor(DCMotorMixin):
    no_load_speed_rpm = 200

motor, encoder = GearMotor(), EncoderMixin()
mcu = Microcontroller("motor-lab", board=MicrocontrollerBoard.ESP32)
mcu.bind_actuator(motor, name="wheel", pwm_pin=5, dir_pin=18)
mcu.bind_sensor(encoder, name="enc", a=32, b=33)
mcu.set_communication(SerialCommunication("/dev/ttyUSB0"))

app = WebApp("motor lab").set_communication(mcu.communication)
app.add_slider("speed (rpm)", target=motor, command="velocity_rpm", maximum=200)
app.add_plot("measured rpm", source=encoder)
app.run()
```

`PythonApp` opens a native window instead of a browser page and `RerunApp`
streams telemetry to the [Rerun](https://rerun.io) viewer instead — all
three take the same `Communication` instance as the microcontroller so both
ends agree, and `EmulatedMicrocontroller` can stand in for real hardware to
drive a physics simulation instead (see Robotics, below). Install with
`uv sync --extra nicegui` (or `--extra rerun`). See
[Microcontroller, Sensors/Actuators definition, and communication](CodeToCAD.md)
in the design doc for I2C/SPI/UART buses, wireless transports and signal
filtering.

<img src="codetocad_integrations/robotics/turtlebot/images/turtlebot_gui.png" width="500">

## Robotics examples: putting it all together

[codetocad_integrations/robotics/](codetocad_integrations/robotics/) contains examples that combine everything above into one script:
**MCAD** (the assembled parts and joint constraints), **ECAD** (the
microcontroller as an `ElectricalComponent` with pin bindings), an **MCU**
definition (run by real firmware or, for simulation, an in-process
`EmulatedMicrocontroller`), and a **WebApp** control panel — all driving the
same physics simulation.

[codetocad_integrations/robotics/turtlebot/](codetocad_integrations/robotics/turtlebot/)
is a differential-drive TurtleBot3 Burger: real chassis/wheel/caster
dimensions, Dynamixel XL430-W250 motor/encoder specs, an ESP32
`Microcontroller` definition, MuJoCo physics with velocity-controlled
wheels, and a `WebApp` with motor sliders and encoder/pose readouts.
Swapping the emulator for a `SerialCommunication` to a real board is the
only change needed to drive physical hardware from the same app.

<img src="codetocad_integrations/robotics/turtlebot/images/turtlebot_drive.gif" width="480">

## User-defined parts

Define a part with the API of your choice (for example
[Build123D](https://build123d.readthedocs.io)):

```python
import build123d
import codetocad

class Box(codetocad.Part3D):
    def build(self):
        length, width, thickness = 80.0, 60.0, 10.0
        with build123d.BuildPart() as ex1:
            build123d.Box(length, width, thickness)

    @codetocad.location
    def example_location(self):
        return codetocad.CubeLocations.top_center.translate(x="2cm", y="2mm")
```

See [CodeToCAD.md](CodeToCAD.md) for the full design document.
