Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ProwlEngine/Prowl.Quill/llms.txt

Use this file to discover all available pages before exploring further.

Prowl.Quill follows a path-then-paint model inspired by HTML5 Canvas and SVG: you first describe a shape using path commands, then paint it with a fill, stroke, or both. A path can contain multiple disconnected sub-paths — each started with MoveTo — and the entire collection is processed together when you call a fill or stroke method. This separation of description from painting lets you build complex, multi-contour shapes (including holes) before committing any geometry to the vertex buffer.

The Path Model

Starting a Path

public void BeginPath()
BeginPath clears all existing sub-paths and resets the current point. Call it whenever you want to start a completely new shape, discarding anything built up since the last BeginPath.

Moving and Drawing

public void MoveTo(float x, float y)
public void LineTo(float x, float y)
public void ClosePath()
MoveTo lifts the “pen” and starts a new sub-path at (x, y) without drawing anything. LineTo extends the current sub-path with a straight line segment to (x, y). ClosePath draws a straight line back to the first point of the current sub-path, closing the contour — essential for filled shapes that should have no gap at the join.
If LineTo is called before any MoveTo, it behaves like MoveTo for the first point, matching HTML Canvas spec behaviour.

Curves

public void Arc(float x, float y, float radius, float startAngle, float endAngle, bool counterclockwise = false)
public void BezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
public void QuadraticCurveTo(float cpx, float cpy, float x, float y)
public void ArcTo(float x1, float y1, float x2, float y2, float radius)
MethodWhat it adds
ArcA circular arc segment centred at (x, y) with the given radius, from startAngle to endAngle in radians.
BezierCurveToA cubic Bézier curve from the current point to (x, y) via two control points. Tessellated adaptively using the current tess_tol.
QuadraticCurveToA quadratic Bézier curve; internally promoted to cubic for consistent tessellation.
ArcToAn arc that is tangent to both the line from current→(x1,y1) and (x1,y1)(x2,y2), rounded to radius.

Painting a Path

Once a path is built, choose how to paint it.

Stroke

public void Stroke()
Strokes every sub-path using the current stroke color, width, joint style, and cap style. Sub-paths with fewer than two points are silently ignored.

Fill Variants

public void Fill()
Fill uses a centroid-fan algorithm: it finds the geometric centre of each sub-path and fans triangles out from it to the perimeter. This is the fastest fill method and produces correct results for convex shapes (rectangles, circles, regular polygons). Avoid it for concave or self-intersecting shapes — the fan triangulation will produce visible artefacts.
// A simple convex triangle — Fill() is ideal here
canvas.BeginPath();
canvas.MoveTo(100, 20);
canvas.LineTo(180, 150);
canvas.LineTo(20, 150);
canvas.ClosePath();
canvas.SetFillColor(Color32.FromArgb(255, 100, 200, 255));
canvas.Fill();

FillAndStroke

public void FillAndStroke()
A convenience method that calls Fill() then Stroke() in a single call, useful for shapes that need both painted interiors and visible outlines.

Winding Mode

public enum WindingMode { OddEven, NonZero }
public void SetSolidity(WindingMode solidity)
WindingMode controls how FillComplex determines which regions of an overlapping or multi-contour path are considered “inside”:

OddEven

A point is inside if a ray cast from it crosses an odd number of path segments. This is the default. A sub-path drawn in the opposite winding direction to the outer contour automatically punches a hole.

NonZero

A point is inside if the signed winding number of the path around it is non-zero. Use this to match SVG’s default fill-rule="nonzero" behaviour.

Examples

Triangle (Convex — use Fill)

canvas.BeginPath();
canvas.MoveTo(100, 20);   // apex
canvas.LineTo(180, 150);  // bottom-right
canvas.LineTo(20, 150);   // bottom-left
canvas.ClosePath();

canvas.SetFillColor(Color32.FromArgb(200, 100, 200, 255));
canvas.SetStrokeColor(Color32.FromArgb(255, 255, 255, 255));
canvas.SetStrokeWidth(2f);
canvas.FillAndStroke();

Rectangle with a Hole (Concave — use FillComplex)

Add the outer contour clockwise and the inner contour counter-clockwise. With the default OddEven winding rule, the inner region becomes a hole.
// Outer rectangle (clockwise)
canvas.BeginPath();
canvas.MoveTo(70, 5);
canvas.LineTo(110, 5);
canvas.LineTo(110, 45);
canvas.LineTo(70, 45);
canvas.ClosePath();

// Inner rectangle hole (counter-clockwise)
canvas.MoveTo(100, 15);
canvas.LineTo(80, 15);
canvas.LineTo(80, 35);
canvas.LineTo(100, 35);
canvas.ClosePath();

canvas.SetFillColor(Color32.FromArgb(255, 255, 150, 100));
canvas.SetStrokeColor(Color32.FromArgb(255, 255, 255, 255));
canvas.SetStrokeWidth(1.5f);

canvas.FillComplex();
canvas.Stroke();

Star-Like Concave Shape

canvas.BeginPath();
canvas.MoveTo(20, 0);   // top point
canvas.LineTo(25, 15);  // right shoulder
canvas.LineTo(40, 15);  // right arm tip
canvas.LineTo(30, 25);  // right indent
canvas.LineTo(35, 40);  // right leg
canvas.LineTo(20, 30);  // bottom centre
canvas.LineTo(5, 40);   // left leg
canvas.LineTo(10, 25);  // left indent
canvas.LineTo(0, 15);   // left arm tip
canvas.LineTo(15, 15);  // left shoulder
canvas.ClosePath();

canvas.SetFillColor(Color32.FromArgb(255, 100, 200, 255));
canvas.SetStrokeColor(Color32.FromArgb(255, 255, 255, 255));
canvas.SetStrokeWidth(1.5f);
canvas.FillComplex();  // Shape is concave — use FillComplex, not Fill
canvas.Stroke();
Fill() uses a fan from the centroid and only works correctly for strictly convex shapes. If your shape is concave (any interior angle greater than 180°) or contains holes, use FillComplex() or FillComplexAA() instead.

Primitive Helpers

For common shapes, Prowl.Quill provides path-building helpers that call BeginPath and set up the contour for you:
// Path-based primitives (call Fill/Stroke after)
canvas.Rect(x, y, width, height);
canvas.RoundedRect(x, y, width, height, radius);
canvas.Circle(x, y, radius);
canvas.Ellipse(x, y, rx, ry);
canvas.Pie(x, y, radius, startAngle, endAngle);
There are also shader-based variants (RectFilled, RoundedRectFilled, CircleFilled, PieFilled) that bypass the path system entirely and emit pre-optimised vertex quads with built-in AA — significantly faster for static shapes.

Build docs developers (and LLMs) love