Implementing Custom Shapes
Relevant source files
The following files were used as context for generating this wiki page:
The guiltty project provides a robust framework for drawing pixel-level 2D graphics in terminal environments. Implementing custom shapes involves leveraging the Shape enum and the drawing primitives provided by the Canvas struct. The core logic resides in guiltty-core, which handles coordinate arithmetic, clipping, and rasterization algorithms independently of specific terminal protocols.
Custom shapes are defined as variants within the Shape enum and are rendered using the Canvas::draw_shape method. This method accepts a Shape and a Fill specification, delegating the actual pixel manipulation to internal rasterization functions like stroke_line, fill_rect, fill_ellipse, or fill_polygon_even_odd.
Sources: crates/guiltty-core/src/lib.rs:1-10, README.md:1-15, crates/guiltty/src/lib.rs:1-8
Core Data Structures
To implement or extend shapes, developers interact with several fundamental types that define geometry and color.
| Type | Description |
|---|---|
Point | A pixel-addressable coordinate (x, y) with top-left origin. |
Rect | A rectangle defined by an origin Point, width, and height. |
Color | RGBA8 color used for pixels and fills. |
Fill | Determines how a shape is painted (currently supports Solid). |
Shape | An enum defining available geometries (Line, Rect, Circle, Ellipse, Triangle, Path). |
Sources: crates/guiltty-core/src/lib.rs:8-80, crates/guiltty-core/src/lib.rs:322-375
The Shape Enum
The Shape enum acts as the primary interface for geometry definition. It includes complex types like Path, which can represent arbitrary polygons.
pub enum Shape {
Line { from: Point, to: Point },
Rect { origin: Point, width: u32, height: u32 },
Circle { center: Point, radius: u32 },
Ellipse { center: Point, rx: u32, ry: u32 },
Triangle { a: Point, b: Point, c: Point },
Path { points: Vec<Point>, closed: bool },
}
Sources: crates/guiltty-core/src/lib.rs:378-406
Drawing Logic and Rasterization
The Canvas struct implements the logic required to translate geometric definitions into pixel data. The following diagram illustrates the flow from a draw_shape call to the pixel buffer.
The drawing process involves coordinate clipping to ensure performance and prevent out-of-bounds access.
Line and Path Drawing
Line segments are rendered using Bresenham's line algorithm. To handle extremely long segments or those partially off-screen, guiltty employs the Liang-Barsky clipping algorithm before walking the pixels.
- Stroked Paths: Open paths are rendered as a series of connected line segments using
stroke_path. - Closed Paths (Polygons): These use a scanline fill algorithm with the even-odd rule.
Sources: crates/guiltty-core/src/lib.rs:431-481, crates/guiltty-core/src/lib.rs:569-633, tasks/plan.md:120-140
Polygon Fill (Even-Odd Rule)
The fill_polygon_even_odd function handles closed Path shapes, including concave and self-intersecting polygons. It calculates scanline crossings at y + 0.5 to avoid vertex-alignment ambiguities.
Ellipses and Triangles
- Ellipses: Use an inclusion test:
(dx/rx)^2 + (dy/ry)^2 <= 1. This is performed usingf64arithmetic to avoid integer overflows with large radii. - Triangles: Rendered using a barycentric sign test within the triangle's bounding box. The
edge_signcalculation usesi128to prevent overflows during cross-product operations at coordinate extremes.
Sources: crates/guiltty-core/src/lib.rs:509-567, crates/guiltty-core/src/lib.rs:815-833
Implementing New Custom Shapes
To add a new shape type to the system, the following architectural steps are required:
- Extend
ShapeEnum: Add the new geometric variant to theShapeenum inguiltty-core/src/lib.rs. - Add Rasterizer Method: Implement a private method on
Canvas(e.g.,fill_trapezoid) that implements the rasterization logic. - Update
draw_shape: Add a match arm toCanvas::draw_shapeto route the new variant to its rasterizer. - Handle Clipping: Ensure the new rasterizer clips its scan range to
[0, width)and[0, height)to prevent unnecessary iterations or panics.
Sources: crates/guiltty-core/src/lib.rs:378-430%60%20and%20%60%5B0%2C%20height)%60%20to%20prevent%20unnecessary%20iterations%20or%20panics.%0A%0ASources%3A%20%5Bcrates/guiltty-core/src/lib.rs%3A378-430), tasks/plan.md:120-140
Summary of Drawing Primitives
| Primitive | Logic | Source Citation |
|---|---|---|
| Line | Bresenham + Liang-Barsky | lib.rs:431-470 |
| Rect | Bounds-clipped fill | lib.rs:484-507 |
| Ellipse | f64 inclusion test | lib.rs:509-536 |
| Triangle | Barycentric sign test (i128) | lib.rs:541-567 |
| Path | Even-odd scanline fill | lib.rs:569-633 |
Sources: crates/guiltty-core/src/lib.rs:431-633
The custom shape system is designed for extensibility and safety, prioritizing precision via high-bit-depth arithmetic (i128, f64) and aggressive clipping to maintain performance in a terminal-rendering context.