Skip to main content

Default Shapes

@oh-just-another/scene ships eleven built-in element types, unioned as BuiltinElement. Each extends ElementBase and adds its own geometry fields.

Built-in typesโ€‹

typeInterfaceExtra fields
rectangleRectangleElementwidth, height
ellipseEllipseElementwidth, height
polygonPolygonElementpoints (local Vec2[])
pathPathElementcommands (PathCommand[])
textTextElementtext, fontFamily, fontSize, maxWidth?, runs? (rich-text overlay)
imageImageElementsrc, fileId?, crop?, width, height
templateTemplateElementtemplateId, data, width, height
groupGroupElement(zero-render container)
frameFrameElementwidth, height, name?
block-arrowBlockArrowElementwidth, height, direction?, headRatio?, bodyThickness?
brushBrushElementpoints (BrushPoint[], per-vertex half-width), closed?

Creating a built-inโ€‹

import { emptyScene, addElement, orderForTop, DEFAULT_LAYER_ID } from "@oh-just-another/scene";
import type { EllipseElement } from "@oh-just-another/scene";
import { elementId } from "@oh-just-another/types";

const ellipse: EllipseElement = {
id: elementId("e1"),
layerId: DEFAULT_LAYER_ID,
type: "ellipse",
position: { x: 40, y: 40 },
rotation: 0,
scale: { x: 1, y: 1 },
order: orderForTop([]),
style: { fill: "#fa0" },
width: 80,
height: 60,
};

const { scene } = addElement(emptyScene(), ellipse);

Brush strokesโ€‹

BrushPoint is { x, y, width } โ€” width is the stroke half-width at that vertex (typically pressure-derived). The renderer builds a single filled outline from the variable-width centreline (brushOutline). Body colour is brushBodyColor(style) = style.stroke ?? style.fill ?? "#000". closed: true marks a looped stroke (set on commit when a fill colour is chosen and the ends meet); its enclosed area is filled with style.fill.

Path commandsโ€‹

PathElement.commands is a list of PathCommands: M (move), L (line), Q (quadratic), C (cubic), and Z (close), all in local coordinates.