A Rust implementation of pikchr aiming for C-pikchr compatibility using zero-cost abstractions.
- Explicit units - Lengths (inches), pixels, scalars, and angles as distinct newtypes prevent unit confusion at compile time.
- Geometry/serialization separation - Layout in inches; convert to pixels once at emit time.
- Centralized state - Single context for variables, directions, current position, object lookup.
- C layout semantics - Match the reference implementation; rScale applied only at emission.
┌─────────────────────────────────────────┐
Primitives │ Length(f64) - inches (canonical) │
│ Px(f64) - pixels (after scaling) │
│ Scalar(f64) - unitless │
│ Angle(f64) - degrees │
│ Color - Named|Rgb|Rgba|Raw │
└─────────────────────────────────────────┘
│
┌───────────────────▼───────────────────┐
Geometry │ Point<T> { x: T, y: T } │
(generic) │ Size<T> { w: T, h: T } │
│ BBox<T> { min: Point, max: Point }│
└───────────────────────────────────────┘
│
┌───────────────────▼───────────────────┐
Aliases │ PtIn = Point<Length> │
│ PtPx = Point<Px> │
│ BoxIn = BBox<Length> │
└───────────────────────────────────────┘
│
┌───────────────────▼───────────────────┐
Conversion │ Scaler { r_scale: f64 } │
│ .len(Length) -> Px │
│ .point(Point<Length>) -> Point<Px> │
└───────────────────────────────────────┘
┌───────┐ ┌────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Parse │───▶│ Expand │───▶│ Evaluate (inches)│───▶│ Emit (pixels) │
│ AST │ │ Macros │ │ LayoutContext │ │ Scaler → SVG │
└───────┘ └────────┘ └──────────────────┘ └─────────────────┘
C pikchr uses Y-up internally and flips to Y-down (SVG) at render time via pik_append_xy:
y = bbox.ne.y - y; // Y-flipRust pikru does the same: internal coordinates are Y-up, to_svg() method handles the flip.
- Primitives:
Length,Px,Scalar,Angle,Colornewtypes - Geometry: Generic
Point<T>,Size<T>,BBox<T> - Scaler:
len(),point(),px(),size(),bbox()methods - Operator traits on
Length - Typed geometry in render.rs:
PtIn,BoxInaliases - Scaler in emitter: All coord/stroke/dash conversions via
Scaler - Y-flip:
Point<Length>::to_svg()withmax_y - ytransformation - Direction vectors:
Direction::unit_vector()andDirection::offset() - Typed evaluator with
EvalValueenum - Angle safety: validated constructors/accessors
- Layout semantics parity (advance/centering rules, chop + arrowheads, sublist local coords)
| Step | Description |
|---|---|
| 3 | Layout semantics: advance, centering, chop/arrowheads, sublist local coords |
| 5 | Drawable<Length> model populated from AST; styles from vars |
| 6 | Emit: dash arrays from dashwid, arrow sizes, stroke widths via Scaler |
| 7 | Print/assert: HTML output with <br> and C-like error lines |
| 8 | Style parity: hex/rgb/rgba colors, fg/bg vars, font-size, class="pikchr" |
| 9 | Margins: margin + thickness + side margins in inches before viewBox |
- Public API stays
pikchr(&str) -> Result<String>returning SVG. - Zero-cost: newtypes wrap
f64; conversions inline; no runtime overhead. - All layout math happens in inch space; pixel conversion is a one-time final pass.