Graphite icon indicating copy to clipboard operation
Graphite copied to clipboard

Build the Appearance table for encoding how vector shapes get rendered

Open Keavon opened this issue 1 year ago • 0 comments

Just as a vector shape has the [Point], [Segment], and [Region] tables for encoding and manipulating the Bezier geometry, a fourth table called [Appearance] will be used to encode how that shape is rendered by the renderer. Each row of this table describes, in order, what rendering procedures to run to draw the appearance of the shape. Each rendering procedure is some function (likely a node, e.g. one for fill and one for stroke) called by the renderer. The final computed shape is given to the function along with all the data cells from the other columns within its row. The shape data describes the final form of what to render, and the extra column cells describe how it should look. Our current representation of a shape with a fill and stroke would become an [Appearance] table with two rows:

AppearanceId Procedure Color Transform Fill Rule Thickness
0 Stroke #00ff00 Identity - 5px
1 Fill #ff00ff Identity NonZero -

As part of implementing this, the Fill and Stroke nodes should be refactored to add rows to the [Appearance] table.

When a vector shape is transformed by the various nodes in Graphite, the points are updated directly which would then affect how a stroke is rendered. To counteract this, those nodes would also multiply in (the inverse of?) that transform for every row's entry in the Transform column in the [Appearance] table. The fill rendering procedure ignores that column because it's not relevant (and likewise, the thickness column). The stroke rendering procedure ignores the fill rule column. Arbitrarily many columns can be added to suit the needs of various rendering procedures, some unique and some general enough (like color) to be shared by multiple rendering procedures.

Since rendering procedures are just nodes, users can create their own in the future. For example, someone could modify the stroke renderer to create a "hairy" effect that draws a scruffy hair or grass kind of look. Or a weather-beaten cracked appearance. Or the whole shape could be drawn like fluffy clouds. Or as a low-poly wireframe. Basically anything should be possible.

The key benefit is that this strategy generalizes the concept of vector strokes and fills, giving customizability in a way that lets the user make modifications to the vector shape data and what will become the final appearance using nodes that modify the table data. Then at the very end, the final form of the shape and the final form of the render parameters can be added. This makes it also very ergonomic to add multiple strokes or multiple fills (useful for gradients or patterns involving transparency, for example). Instead of arbitrary fill and stroke rendering routines, this opens up the possibilities through a better generalized model of what styling a shape is about. Otherwise, shapes would just be invisible mathematical constructs.

Keavon avatar Sep 23 '24 07:09 Keavon