Skip to content
D
Documentation

Svg — functions p–w

reference
5 min readUpdated

Functions P–W

Import these from @grafloria/renderer.

Functions

pointAtPositionOnPolyline

Arc-length interpolation of a point at position t (0-1) along a polyline. Mirrors {@link LinkModel.getPointAtPosition }'s polyline fallback so label anchors line up with what the renderer draws. Returns null for < 2 points.

ts
function pointAtPositionOnPolyline(points: Point[], t: number): Point | null

portLabelGeometry

Where does the label go, which way does it point, and how does it hang off its anchor?

The align/valign choice matters as much as the position: a label to the LEFT of a port must be right-aligned (end), or it grows away from the port and collides with the node.

ts
function portLabelGeometry(input: PortLabelInput): PortLabelGeometry

portLabelWidth

The label's rendered width, using the shared text engine's estimator.

ts
function portLabelWidth(spec: PortLabelSpec, fontSize: number): number

portLayoutNames

For tests / tooling.

ts
function portLayoutNames(): string[]

portPositionInBounds

Same answer, for callers that already hold the node's bounding box.

ts
function portPositionInBounds(
  port: PortModel,
  node: NodeModel,
  bounds: BoundingBox
): { x: number; y: number }

portWorldPosition

The port's WORLD position — for hit-testing, magnets and link endpoints alike. Everything that needs "where is this port on screen" comes through here, so what you click is always what you see.

ts
function portWorldPosition(port: PortModel, node: NodeModel): { x: number; y: number }

registerLabelTemplate

ts
function registerLabelTemplate(name: string, template: LabelTemplate): void

registerLinkTemplate

ts
function registerLinkTemplate(name: string, template: LinkTemplate): void

registerMarker

ts
function registerMarker(name: string, definition: MarkerDefinition): void

registerNotationShapes

Register every notation shape that the base registry does not already own.

ts
function registerNotationShapes(): void

registerPathShape

Register an arbitrary SVG-path shape. This is the payoff of the geometry contract: a userland caller adds a brand-new silhouette in ONE call and it works everywhere the built-ins do — node body, selection highlight, drop shadow, smart-connection boundary and geometry-true port anchors — with NO core-team switch edits.

ts
// Parametric — a 5-point star that fills any box.
registerPathShape('star', (w, h) => starPath(w, h));
// Static — a chevron authored in a 24×24 art box.
registerPathShape('chevron', 'M2,4 L14,4 L22,12 L14,20 L2,20 L10,12 Z',
  { viewBox: { x: 0, y: 0, w: 24, h: 24 } });
ts
function registerPathShape(
  type: string,
  path: PathGeometry,
  opts: PathShapeOptions = {}
): void

registerPortLayout

Register a custom strategy. Hosts can add their own; the built-ins are just entries.

ts
function registerPortLayout(name: string, strategy: PortLayoutStrategy): void

registerShape

Register (or replace) a shape definition. This is the payoff: a new shape is added in ONE place and immediately works across body/selection/shadow, smart-connection boundaries and port positioning — no switch edits.

ts
function registerShape(type: string, def: Omit<ShapeDefinition, 'type'> & { type?: string }): void

renderNodePanel

Build the panel overlay VNodes for a node, in draw order (background bands first, corner overlays last). Returns [] when the node has no panel. These are appended to the node group ON TOP of the base shape body, so the panel rides whatever silhouette the shape registry drew.

ts
function renderNodePanel(
  node: NodeModel,
  width: number,
  height: number,
  ctx: PanelRenderContext
): VNode[]

renderPortGlyph

Build the port's glyph VNode.

Every existing diagram's port VNodes are unchanged.

ts
function renderPortGlyph(input: PortGlyphInput): VNode

renderPortLabel

Render the port's label as a <text> (or a <g> when it is rotated — a rotation needs a transform, and putting it on the text itself would fight the x/y the text-block engine emits for its tspans).

ts
function renderPortLabel(input: PortLabelInput): VNode

renderTextBlock

Render a text block as a single VNode.

  • one line → a carrying textContent (+ dominant-baseline for vertical centering), preserving the historical single-line output.
  • many lines → a with one per line, vertically aligned via per-line dy (first line offset by {@link baselineOffset}).
ts
function renderTextBlock(opts: TextBlockOptions): VNode

resolveAspectRatio

The explicit aspect ratio (w÷h) a node is locked to, or null when unlocked. aspectLock: true resolves against a supplied current size.

ts
function resolveAspectRatio(
  sizing: NodeSizing,
  current?: { width: number; height: number }
): number | null

resolveRotation

Total label rotation, with keep-upright auto-flip.

angle rotates the label. keepUpright (default ON) then adds 180° to any label that would end up reading upside-down — the standard trick, and the reason a radial label on the LEFT half of a circle still reads left-to-right instead of mirror-written.

ts
function resolveRotation(spec: PortLabelSpec, direction: { x: number; y: number }): number

resolveSpot

Resolve spot to a world point on the glyph box, plus the direction the link travels there.

Nothing moves unless the author asks it to.

ts
function resolveSpot(
  spot: PortSpot | undefined,
  input: SpotInput
): { point: { x: number; y: number }; direction: Side }

resolveToolbar

The effective toolbar config for a node: the node's own metadata, with a host-supplied resolver (per-type policy) layered on top.

ts
function resolveToolbar(node: NodeModel, resolver?: ToolbarResolver): NodeToolbarConfig

runPortLayout

Run a port's layout: pick the strategy, apply it, then the shared dx/dy nudge and optional rotation compensation.

ts
function runPortLayout(spec: PortLayoutSpec | undefined, input: PortLayoutInput): { x: number; y: number }

sampleOutlineFromData

Parse + sample a d string in one call.

ts
function sampleOutlineFromData(d: string, steps = 24): Point[]

sampleOutlinePoints

Sample a path's OUTLINE as a dense polygon of points.

The path is flattened (curves → line segments) and the single richest subpath — the shape's main silhouette — is returned as a vertex ring. Interior detail subpaths (a UML component's tabs, a cube's inner edges) are intentionally dropped: the boundary/anchor scanline treats its input as ONE closed ring, and stitching several subpaths into one ring would invent phantom edges.

Returns an empty array for an unparseable / empty d so callers fall back to the bounding box.

ts
function sampleOutlinePoints(cmds: PathCmd[], steps = 24): Point[]

sanitizeAssetUrl

Allow only image-safe URL schemes on an asset href. Blocks javascript: and data:text/html (the executable-URL vectors); permits data:image/*, blob:, http(s):, and scheme-relative / path references. An unsafe or non-string href resolves to '' so the slot renders empty instead of dangerous.

ts
function sanitizeAssetUrl(href: unknown): string

sanitizeHtmlContent

Convert ONE sanitized content node to a VNode. Unknown tags collapse to a <span> (their text is kept, their dangerous identity is dropped). Returns null for a node that contributes nothing.

ts
function sanitizeHtmlContent(spec: HtmlContentNode): VNode | null

segmentIntersectsRect

Liang-Barsky: does the segment touch the rect at all?

ts
function segmentIntersectsRect(a: Point, b: Point, rect: OptimizerRect): boolean

separateParallelRoute

Push a routed polyline onto its lane in a parallel bundle.

The two ENDPOINTS never move: they are the ports, and a fan that pulled the line off its port would be worse than the overlap it fixes. Only the interior of the route is displaced.

• orthogonal — every INTERIOR segment is slid along its own normal. A segment's neighbours run perpendicular to it, so sliding it only makes them longer or shorter: the route stays exactly orthogonal (no diagonal ever appears). A route with no interior segment (a straight 2-point orthogonal run between aligned ports) gets an S-jog inserted instead, because there is otherwise nothing to displace. • direct / smooth / bezier — the interior points are displaced along the bundle normal; a bare 2-point route gets an offset midpoint, which the path emitters turn into a bow (a spline for smooth, a shallow V for direct). This is the "curviness" fan GoJS and React Flow draw.

ts
function separateParallelRoute(
  points: FanoutPoint[],
  offset: number,
  normal: FanoutPoint,
  pathType: string
): FanoutPoint[]

serializePathCmds

Serialize a command list back into a compact d string.

ts
function serializePathCmds(cmds: PathCmd[]): string

sideNormal

Outward unit normal of a node side.

ts
function sideNormal(side: FanoutSide): FanoutPoint

sideTangent

Unit tangent of a side — the axis links spread along.

ts
function sideTangent(side: Side): { x: number; y: number }

spreadOffsets

The signed lane offsets for count links sharing one port, spaced spacing apart and CENTRED on the port's own attachment point.

count 1 → [0] ← a lone link NEVER moves. This is the whole byte-stability guarantee: a port with one link renders exactly where it always did. count 2 → [-s/2, +s/2] count 3 → [-s, 0, +s]

ts
function spreadOffsets(count: number, spacing: number, max = 0): number[]

toolbarAllows

Whether a boolean tool (resize / rotate / remove) is enabled; def when unset.

ts
function toolbarAllows(
  config: NodeToolbarConfig,
  tool: 'resize' | 'rotate' | 'remove',
  def = true
): boolean

translateCmds

Offset every coordinate by (dx, dy) — used for grow/shadow transforms.

ts
function translateCmds(cmds: PathCmd[], dx: number, dy: number): PathCmd[]

unregisterLabelTemplate

Remove one label template. Returns false when it was not registered.

ts
function unregisterLabelTemplate(name: string): boolean

unregisterLinkTemplate

Remove one link template. Returns false when it was not registered.

ts
function unregisterLinkTemplate(name: string): boolean

unregisterMarker

Remove one marker. Returns false when it was not registered.

ts
function unregisterMarker(name: string): boolean

unregisterShape

Remove a registered shape. Returns false when the type was not registered.

ts
function unregisterShape(type: string): boolean

wrapText

Break text into display lines: split on hard '\n', then greedily word-wrap each paragraph to maxWidth. A single word wider than maxWidth breaks at its HYPHENS first ("predefined-process" → "predefined-" + "process" — a hyphen is a legitimate break point; a centred clip eats BOTH ends of the word and reads as gibberish, which is exactly what the screenshot audit caught). A word with no hyphen is kept whole (clipped, not broken) — the historical LabelRenderer behavior for genuinely unbreakable runs.

ts
function wrapText(text: string, maxWidth: number | undefined, fontSize: number): string[]

Was this page helpful?

Svg — functions p–w — Grafloria