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.
tsfunction 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.
tsfunction portLabelGeometry(input: PortLabelInput): PortLabelGeometry
portLabelWidth
The label's rendered width, using the shared text engine's estimator.
tsfunction portLabelWidth(spec: PortLabelSpec, fontSize: number): number
portLayoutNames
For tests / tooling.
tsfunction portLayoutNames(): string[]
portPositionInBounds
Same answer, for callers that already hold the node's bounding box.
tsfunction 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.
tsfunction portWorldPosition(port: PortModel, node: NodeModel): { x: number; y: number }
registerLabelTemplate
tsfunction registerLabelTemplate(name: string, template: LabelTemplate): void
registerLinkTemplate
tsfunction registerLinkTemplate(name: string, template: LinkTemplate): void
registerMarker
tsfunction registerMarker(name: string, definition: MarkerDefinition): void
registerNotationShapes
Register every notation shape that the base registry does not already own.
tsfunction 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 } });
tsfunction registerPathShape(
type: string,
path: PathGeometry,
opts: PathShapeOptions = {}
): void
registerPortLayout
Register a custom strategy. Hosts can add their own; the built-ins are just entries.
tsfunction 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.
tsfunction 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.
tsfunction 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.
tsfunction 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).
tsfunction renderPortLabel(input: PortLabelInput): VNode
renderTextBlock
Render a text block as a single
- 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}).
tsfunction 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.
tsfunction 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.
tsfunction 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.
tsfunction 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.
tsfunction 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.
tsfunction runPortLayout(spec: PortLayoutSpec | undefined, input: PortLayoutInput): { x: number; y: number }
sampleOutlineFromData
Parse + sample a d string in one call.
tsfunction 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.
tsfunction 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.
tsfunction 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.
tsfunction sanitizeHtmlContent(spec: HtmlContentNode): VNode | null
segmentIntersectsRect
Liang-Barsky: does the segment touch the rect at all?
tsfunction 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.
tsfunction separateParallelRoute(
points: FanoutPoint[],
offset: number,
normal: FanoutPoint,
pathType: string
): FanoutPoint[]
serializePathCmds
Serialize a command list back into a compact d string.
tsfunction serializePathCmds(cmds: PathCmd[]): string
sideNormal
Outward unit normal of a node side.
tsfunction sideNormal(side: FanoutSide): FanoutPoint
sideTangent
Unit tangent of a side — the axis links spread along.
tsfunction 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]
tsfunction spreadOffsets(count: number, spacing: number, max = 0): number[]
toolbarAllows
Whether a boolean tool (resize / rotate / remove) is enabled; def when unset.
tsfunction toolbarAllows(
config: NodeToolbarConfig,
tool: 'resize' | 'rotate' | 'remove',
def = true
): boolean
translateCmds
Offset every coordinate by (dx, dy) — used for grow/shadow transforms.
tsfunction translateCmds(cmds: PathCmd[], dx: number, dy: number): PathCmd[]
unregisterLabelTemplate
Remove one label template. Returns false when it was not registered.
tsfunction unregisterLabelTemplate(name: string): boolean
unregisterLinkTemplate
Remove one link template. Returns false when it was not registered.
tsfunction unregisterLinkTemplate(name: string): boolean
unregisterMarker
Remove one marker. Returns false when it was not registered.
tsfunction unregisterMarker(name: string): boolean
unregisterShape
Remove a registered shape. Returns false when the type was not registered.
tsfunction 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.
tsfunction wrapText(text: string, maxWidth: number | undefined, fontSize: number): string[]
Was this page helpful?