Sign in

    #BPMN geometry

    layout(view, policy=PreferDi, limits=Limits::new()) is a pure operation over moonrockz/diagrams/bpmn. It returns optional immutable geometry and diagnostics. It has no XML parser, DOM, clock or I/O dependency.

    • PreferDi uses complete supplied geometry or computes the entire view when bounds or waypoints are missing. Invalid supplied values are errors.
    • RequireDi returns no geometry unless required supplied geometry is complete.
    • Automatic ignores supplied coordinates and computes the entire view.

    Geometry.shapes and connections retain source model values, exact bounds and waypoints alongside numeric boxes. Labels retain exact bounds and font settings. A single translate_x/translate_y and canvas extent enclose every visible shape, label and waypoint with padding. Renderers apply that translation once. Hidden subprocess contents are omitted using the core model's shared Bpmn::scope visibility. Boundary events inherit their activity's visibility. Message connections may use visible display anchors while their source flow keeps its original semantic endpoint identities.

    Supplied numeric tokens are preserved verbatim. Layout calculations require finite coordinates of magnitude at most one billion and positive shape extents. svg_number formats generated numbers to three decimal places with deterministic half-away-from-zero rounding. Limits bound visible nodes, edges, work and output. Failure returns no partial geometry.

    #Automatic layout

    Each containment scope uses iterative strongly connected component condensation and ranks the resulting acyclic graph. Source order breaks ties. Measured task labels and external captions determine column widths and row heights. Pools and nested lanes form bands; expanded subprocesses have their own scopes. Connected data/artifacts can follow flow nodes into lanes while keeping their subprocess ownership. Category membership in Ownership.groups reserves shared member bands and determines group frames. Disconnected components pack in source order.

    Boundary events are placed after their activity and reserve caption space. Connections use actual shape ports, with distinct ports for parallel edges. Circles, diamonds and stores use their outlines rather than bounding-box corners. The orthogonal router tries simple channels, then a bounded coordinate-grid A* search around unrelated shapes and label boxes. Return edges and self-loops can use outer channels. Connections whose endpoints share a pool or expanded scope stay within that scope. Cross-pool messages keep their declared visible endpoints. Connection labels avoid shapes, other labels and previously routed segments; subsequent connections route around them. No external layout service is used.

    The default bounds are 10,000 visible nodes, 50,000 connections, one million work units and 16 Mi UTF-16 output units. Increase Limits explicitly for larger searches. A dense graph can exhaust search before it reaches a node/edge bound; that produces bpmn/layout-limit and no partial layout. The frozen corpus has no legibility findings in either palette. Arbitrary inputs are not promised an optimal embedding or zero line crossings.

    check(geometry, palette) returns bounded, read-only findings for unintended shape, label and route intersections, text fit and minimum size, contrast, ownership containment, boundary attachment and endpoint attachment. Exhaustion is explicit. Supplied geometry is never changed. Pools, lanes, expanded scopes, groups and their contained artifacts can overlap intentionally. Data and artifacts inside lanes use visual containment because they cannot have a flowNodeRef.

    Use bpmn_palette(theme.dark) or its light counterpart to obtain the shared BPMN family hues with text shades that meet 4.5:1 against background and family fill. The sequence renderer continues to use its existing palettes and layout package.

    #Example

    ///|
    test "compute and check a semantic view without DI" {
    let view=@model.Bpmn::new("P",[
    @model.Node::new("Start",StartEvent,label="Start"),
    @model.Node::new("Review",UserTask,label="Review request"),
    ],[@model.Flow::new("Request","Start","Review",label="request")])
    let result=@layout.layout(view,policy=Automatic)
    let geometry=result.geometry.unwrap()
    assert_eq(result.diagnostics,[])
    assert_eq(geometry.source,Automatic)
    assert_eq(@layout.check(geometry,@layout.bpmn_palette(@theme.Theme::classic().dark)),[])
    }

    Connection

    pub struct Connection {
    flow :
    Flow

    index : Int
    source : String
    target : String
    points : ReadOnlyArray[
    ExactPoint
    ]
    line : ReadOnlyArray[Point]
    label : Label?
    } derive(Eq,
    Debug
    )

    Connection::equal

    fn Connection::equal(Connection, Connection) -> Bool

    Connection::not_equal

    fn Connection::not_equal(x : Connection, y : Connection) -> Bool

    Finding

    pub struct Finding {
    rule : Rule
    identities : ReadOnlyArray[String]
    message : String
    } derive(Eq,
    Debug
    )

    Finding::equal

    fn Finding::equal(Finding, Finding) -> Bool

    Finding::not_equal

    fn Finding::not_equal(x : Finding, y : Finding) -> Bool

    Finding::to_repr

    Geometry

    pub struct Geometry {
    view :
    Bpmn

    shapes : ReadOnlyArray[Shape]
    connections : ReadOnlyArray[Connection]
    source : Source
    translate_x : Int64
    translate_y : Int64
    width : Int64
    height : Int64
    } derive(Eq,
    Debug
    )

    Geometry::equal

    fn Geometry::equal(Geometry, Geometry) -> Bool

    Geometry::not_equal

    fn Geometry::not_equal(x : Geometry, y : Geometry) -> Bool

    Geometry::to_repr

    Label

    pub struct Label {
    text : String
    font :
    Font

    bounds :
    ExactBounds
    ?
    box : Rect
    size : Double
    weight : Int
    rotation : Int
    mask : Bool
    owner : String
    family :
    Family
    ?
    } derive(Eq,
    Debug
    )

    Label::equal

    fn Label::equal(Label, Label) -> Bool

    Label::not_equal

    fn Label::not_equal(x : Label, y : Label) -> Bool

    Label::to_repr

    Limits

    pub struct Limits {
    nodes : Int
    edges : Int
    work : Int
    output_units : Int
    } derive(Eq,
    Debug
    )

    Limits::equal

    fn Limits::equal(Limits, Limits) -> Bool

    Limits::new

    fn Limits::new(nodes? : Int, edges? : Int, work? : Int, output_units? : Int) -> Limits

    Limits::not_equal

    fn Limits::not_equal(x : Limits, y : Limits) -> Bool

    Limits::to_repr

    Point

    pub(all) struct Point {
    x : Double
    y : Double
    } derive(Eq,
    Debug
    )

    Point::equal

    fn Point::equal(Point, Point) -> Bool

    Point::not_equal

    fn Point::not_equal(x : Point, y : Point) -> Bool

    Point::to_repr

    Rect

    pub(all) struct Rect {
    x : Double
    y : Double
    width : Double
    height : Double
    } derive(Eq,
    Debug
    )

    Rect::contains

    fn Rect::contains(self : Rect, other : Rect, tolerance? : Double) -> Bool

    Rect::equal

    fn Rect::equal(Rect, Rect) -> Bool

    Rect::not_equal

    fn Rect::not_equal(x : Rect, y : Rect) -> Bool

    Rect::overlaps

    fn Rect::overlaps(self : Rect, other : Rect) -> Bool

    Rect::to_repr

    Result

    pub struct Result {
    geometry : Geometry?
    diagnostics : ReadOnlyArray[
    Diagnostic
    ]
    } derive(Eq,
    Debug
    )

    Result::equal

    fn Result::equal(Result, Result) -> Bool

    Result::not_equal

    fn Result::not_equal(x : Result, y : Result) -> Bool

    Result::to_repr

    Rule

    pub(all) enum Rule {
    Overlap
    Fit
    Size
    Contrast
    Containment
    Attachment
    Endpoint
    Limit
    } derive(Eq,
    Debug
    )

    Rule::equal

    fn Rule::equal(Rule, Rule) -> Bool

    Rule::not_equal

    fn Rule::not_equal(x : Rule, y : Rule) -> Bool

    Rule::to_repr

    Shape

    Shape::equal

    fn Shape::equal(Shape, Shape) -> Bool

    Shape::not_equal

    fn Shape::not_equal(x : Shape, y : Shape) -> Bool

    Shape::to_repr

    Source

    pub(all) enum Source {
    Supplied
    Automatic
    } derive(Eq,
    Debug
    )

    Source::equal

    fn Source::equal(Source, Source) -> Bool

    Source::not_equal

    fn Source::not_equal(x : Source, y : Source) -> Bool

    Source::to_repr

    activity

    fn activity(kind :
    NodeKind
    ) -> Bool

    bpmn_palette

    Preserve family hues while checking text against opaque canvas and the actual coloured body. Sequence palette values are unchanged.

    check

    fn check(geometry : Geometry, palette :
    Palette
    , work_limit? : Int) -> ReadOnlyArray[Finding]

    Read-only findings never move supplied geometry. Search exhaustion is an explicit finding rather than a silently incomplete legibility result.

    layout

    Use all supplied coordinates or compute a complete view. Invalid supplied values never become a partial fallback. Automatic explicitly ignores DI.

    midpoint

    fn midpoint(line : ArrayView[Point]) -> Point?

    Half the polyline's measured length, rather than its middle vertex.

    svg_number

    fn svg_number(value : Double) -> String?

    Canonical derived SVG numbers have at most three fractional digits. Authored supplied coordinates bypass this formatter and keep their exact tokens.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io