Logo FS-Skia-UI

Scene

FS.Skia.UI.Scene is the framework's drawing vocabulary: a small, immutable, data-only description of what to draw, with no knowledge of how it gets drawn. A Scene is a tree of SceneNode values — rectangles, circles, paths, text, images, clips, gradients, transforms — built with pure constructor functions and consumed by the SkiaViewer host, which is the only place those nodes turn into Skia draw calls. Keeping the vocabulary in its own package with no rendering dependency is what lets a view : 'model -> Scene function be a pure value you can build, inspect, hash, and diff in tests without ever touching a GPU. Per ADR 0008, this package is the single canonical source of the scene vocabulary — there is exactly one Scene type, retyped throughout the host with no conversion shim.

See the API reference for the full surface: FS.Skia.UI.Scene, Scene, and the reference index.

The core types

A scene is built from three mutually-recursive types:

type SceneNode =
    | Empty
    | Group of Scene list
    | Rectangle of (float * float * float * float) * Color
    | PaintedRectangle of Rect * Paint
    | Circle of center: Point * radius: float * fill: Color
    | Ellipse of Rect * Paint
    | Line of Point * Point * Paint
    | Path of PathSpec * Paint
    | Points of Point list * Paint
    | Vertices of VertexMode * Vertex list * Paint
    | Arc of Rect * float * float * Paint
    | Text of (float * float) * string * Color
    | TextRun of TextRun
    | Image of (float * float * float * float) * string
    | ClipNode of Clip * Scene
    | RegionNode of Region * Paint
    | ColorSpaceNode of ColorSpace * Scene
    | PerspectiveNode of PerspectiveTransform * Scene
    | PictureNode of Picture
    | Chart of values: float list
    // … (the full set is exhaustive in the .fsi)

and Scene = { Nodes: SceneNode list }

and Picture = { Name: string; Scene: Scene }

SceneNode is the unit of drawing; Scene is just an ordered list of nodes (painted back-to-front); Picture is a named, reusable sub-scene. Several nodes — Group, ClipNode, ColorSpaceNode, PerspectiveNode, PictureNode — nest a child Scene, so the whole structure is a tree.

Around those sit the supporting value types, all plain records and unions:

The builder modules

You rarely construct SceneNode cases by hand. Five modules provide ergonomic, self-describing constructors:

Two of the Scene constructors deserve a note: filledRectangle (a Rect-based sibling of the positional rectangle) and textAt (a Point-based sibling of the positional text) exist specifically to avoid the silent arity slip that bare (float * float * float * float) and (float * float) tuples invite — a small but deliberate ergonomics-and-correctness choice baked into the public surface.

Inspection without pixels

Because a scene is pure data, the package ships functions that interrogate it semantically, which is how the framework's tests assert coverage without comparing rendered images:

Animation: motion as data

Animation.fsi adds a bounded, additive motion slice on top of the static vocabulary (feature 073). The design rule is that sampling is a pure function of an explicit TimeSpan — identical inputs and identical time samples always produce byte-identical output, and the framework owns no hidden mutable animation registry.

How it fits the rest of the framework

Analysis

Implementation strengths

Implementation weaknesses

Design pros

Design cons

type SceneNode = | Empty | Group of Scene list | Rectangle of (float * float * float * float) * obj | PaintedRectangle of obj * obj | Circle of center: obj * radius: float * fill: obj | Ellipse of obj * obj | Line of obj * obj * obj | Path of obj * obj | Points of obj * obj | Vertices of obj * obj * obj ...
type Scene = { Nodes: SceneNode list }
type 'T list = List<'T>
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
type Picture = { Name: string Scene: Scene }

Type something to start searching.