Line-based geometry toolkit with chained effects and real-time preview.
Project description
Grafix
Grafix is a Python-based creative coding framework compatible with pen plotter.
Press G at any time while a sketch is running to export a G-code (.gcode) file.
Installation
pip install grafix
macOS-first. Tested on macOS (Apple Silicon). Other platforms are not officially supported yet.
Quick start
from grafix import E, G, run
def draw(t: float):
# All arguments automatically appear in the parameter GUI
geometry = G.polyhedron()
effect = E.fill().subdivide().displace().rotate(rotation=(t * 6, t * 5, t * 4))
return effect(geometry)
if __name__ == "__main__":
run(draw, canvas_size=(148, 210), render_scale=5.0)
Examples
Core API
Glets you generate primitives such assphere,polyhedron,grid, and more.Elets you modulate primitives such asaffine,fill,repeat, and more.runlets you render a user-defineddraw(t)function on each frame.
Optional features
Llets you define layers (stroke color, thickness, etc.).cclets you map MIDI CC messages to any parameter.@primitivelets you register custom primitives (they become available underG).@effectlets you register custom effects (they become available underE).@presetlets you register reusable components (only selected params are exposed to the Parameter GUI).Plets you call registered presets asP.<name>(...).Exportprovides a headless export entrypoint.Parameter GUIlets you tweak parameters live while the sketch is running.- Keyboard shortcuts let you export output quickly:
Psaves a.pngimageSsaves a.svgfileVrecords an.mp4videoGsaves a.gcodefile for pen plotters
Extending (custom primitives / effects)
You can register your own primitives and effects via decorators:
from grafix.api import effect, primitive
@primitive
def user_prim(*, r=10.0):
...
@effect
def user_eff(inputs, *, amount=1.0):
...
Notes:
- Built-in primitives/effects must provide
meta=...(enforced). - For user-defined ops,
metais optional. If omitted, parameters are not shown in the Parameter GUI.
Presets (reusable components)
Use @preset to register a component, and call it via P.<name>(...):
from grafix import P, preset
@preset(meta={"scale": {"kind": "float", "ui_min": 0.1, "ui_max": 10.0}})
def grid_system_frame(*, n_rows: int=5, r_cols: int=8, name=None, key=None):
...
P.grid_system_frame()
For IDE completion of P.<name>(...), regenerate stubs after adding/changing presets:
python -m grafix stub
Configuration
A config.yaml lets you locate external fonts and choose where Grafix writes runtime outputs (.svg, .png, .mp4, .gcode).
Grafix starts from the packaged defaults (grafix/resource/default_config.yaml) and then overlays user configs.
Config overlay order (later wins):
- packaged defaults:
grafix/resource/default_config.yaml ./.grafix/config.yaml(project-local)~/.config/grafix/config.yaml(per-user)run(..., config_path="path/to/config.yaml")
Paths support ~ and environment variables like $HOME.
To create a project-local config (starting from the packaged defaults):
mkdir -p .grafix
python -c "from importlib.resources import files; print(files('grafix').joinpath('resource','default_config.yaml').read_text())" > .grafix/config.yaml
$EDITOR .grafix/config.yaml
Config overlay is a top-level shallow update (no deep merge). If you override export:, keep both export.png and export.gcode
blocks from the packaged defaults.
To autoload user presets from a directory:
paths:
preset_module_dirs:
- "sketch/presets"
To configure G-code export defaults (used when calling export_gcode(..., params=None)):
export:
gcode:
origin: [0.0, 0.0]
y_down: false
To prioritize MIDI device connections when using midi_port_name="auto":
midi:
inputs:
- port_name: "Grid"
mode: "14bit"
- port_name: "TX-6 Bluetooth"
mode: "7bit"
- port_name: "auto"
mode: "7bit"
Not implemented yet
- LFOs to modulate any parameters with rhythm
Development
Geometry (the core data model)
Grafix is built around an immutable Geometry node, which represents a recipe (not yet realized polylines).
Nodes form a DAG (directed acyclic graph):
op: the operator name (primitive/effect/concat are stored uniformly)inputs: childGeometrynodes (empty for primitives)args: normalized(name, value)pairsid: a content-based signature derived from(op, inputs, args)
Primitives (G.*) create leaf Geometry nodes. Effects (E.*) take one or more input Geometrys and return a new Geometry
that references them. Chaining operations in draw(t) builds the DAG.
When parameter_gui is enabled, the GUI edits the parameters (args) of ops. Updating a parameter creates new Geometry nodes
with new ids, while unchanged subgraphs keep their ids — which makes caching/reuse straightforward during interactive previews.
RealizedGeometry (what primitives/effects compute)
Evaluating the Geometry DAG produces RealizedGeometry, a compact polyline representation:
coords:np.ndarrayoffloat32, shape(N, 3)(x, y, z). A 2D(N, 2)array is also accepted (z=0 is implied).offsets:np.ndarrayofint32, shape(M+1,), where polylineiiscoords[offsets[i]:offsets[i+1]].offsets[0]is0andoffsets[-1]equalsN.
Custom primitives return a RealizedGeometry. Custom effects take Sequence[RealizedGeometry] (usually 1 input) and return a new
RealizedGeometry. Treat input arrays as immutable (writeable=False) and avoid in-place mutation.
Dev tools (optional):
pip install -e ".[dev]"
Run a sketch:
python sketch/readme.py
List built-in ops:
python -m grafix list effects
python -m grafix list primitives
python -m grafix list
Regenerate API stubs (for P.<name>(...) completion):
python -m grafix stub
Headless export (PNG):
python -m grafix export --callable your_module:draw --t 0.0
python -m grafix export --callable your_module:draw --t 0.0 1.0 2.0 --out-dir data/output
Note: --callable module:attr follows Python import rules (make sure the module is importable from your current working directory / PYTHONPATH).
Benchmark (effect perf + report):
python -m grafix benchmark -- --help
Dependencies
Core (default):
- numpy
- numba
- shapely
- pyclipper
- moderngl
- pyglet
- imgui
- fontPens
- fontTools
- PyYAML
- mido
- python-rtmidi
- psutil
External:
- resvg (svg to png)
- ffmpeg (video encoding)
Dev (optional):
- pytest
- ruff
- mypy
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file grafix-0.0.5.tar.gz.
File metadata
- Download URL: grafix-0.0.5.tar.gz
- Upload date:
- Size: 3.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d90c77abcaef1b92329eda1ebd2c1bdd07365b7a31b2eaeeb708ea61758af007
|
|
| MD5 |
f83fc91a3364287ab96a970539b78ce8
|
|
| BLAKE2b-256 |
27263d9dc586ec18d35269f9a8700f5dd7c0623ef5076315d03658f1d9922d77
|
File details
Details for the file grafix-0.0.5-py3-none-any.whl.
File metadata
- Download URL: grafix-0.0.5-py3-none-any.whl
- Upload date:
- Size: 3.6 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1342e3acc62eed93ed048e8ae025617ef7e45771efe87a6068891c395b8f30e5
|
|
| MD5 |
fbd7908503d6bd9a3951ff6b07d69c64
|
|
| BLAKE2b-256 |
88ff9ac8931987412bd410b163f716b83e465d597ff46e90fcc2b8792b1865e8
|