///|
/// Pure swimlane geometry has no dependency on BPMN document or geometry types.
pub struct Limits {
nodes : Int32
edges : Int32
depth : Int32
work : Int32
output_units : Int32
} derive(Eq, Debug)
///|
pub fn Limits::new(
nodes~ : Int32 = 10000,
edges~ : Int32 = 50000,
depth~ : Int32 = 256,
work~ : Int32 = 1000000,
output_units~ : Int32 = 16777216,
) -> Limits {
{ nodes, edges, depth, work, output_units, }
}
///|
pub(all) struct Point {
x : Double
y : Double
} derive(Eq, Debug)
///|
pub(all) struct Rect {
x : Double
y : Double
width : Double
height : Double
} derive(Eq, Debug)
///|
pub fn Rect::contains(
self : Rect,
other : Rect,
tolerance~ : Double = 0.0,
) -> Bool {
other.x >= self.x - tolerance &&
other.y >= self.y - tolerance &&
other.x + other.width <= self.x + self.width + tolerance &&
other.y + other.height <= self.y + self.height + tolerance
}
///|
pub fn Rect::overlaps(self : Rect, other : Rect) -> Bool {
self.x < other.x + other.width &&
other.x < self.x + self.width &&
self.y < other.y + other.height &&
other.y < self.y + self.height
}
///|
pub(all) enum Kind {
Lane
Group
Node
} derive(Eq, Debug)
///|
pub struct Label {
text : String
box : Rect
size : Double
weight : Int32
owner : String
family : @theme.Family?
} derive(Eq, Debug)
///|
pub struct Shape {
id : String
index : Int32
kind : Kind
shape : String
box : Rect
outline_box : Rect
label : Label?
parent : String?
lane : String?
expanded : Bool
} derive(Eq, Debug)
///|
pub struct Connection {
connection : @model.Connection
index : Int32
source : String
target : String
line : ReadOnlyArray[Point]
label : Label?
} derive(Eq, Debug)
///|
pub struct Geometry {
view : @model.Swimlane
shapes : ReadOnlyArray[Shape]
connections : ReadOnlyArray[Connection]
width : Int32
height : Int32
} derive(Eq, Debug)
///|
pub struct Result {
geometry : Geometry?
diagnostics : ReadOnlyArray[@diagrams.Diagnostic]
} derive(Eq, Debug)
///|
priv struct Budget {
limits : Limits
diagnostics : Array[@diagrams.Diagnostic]
mut work : Int32
mut failed : Bool
}
///|
fn Budget::error(self : Budget, code : String, message : String) -> Unit {
if !self.failed {
self.diagnostics.push(@diagrams.Diagnostic::error(code, message))
}
self.failed = true
}
///|
fn Budget::charge(self : Budget, amount : Int32) -> Bool {
if amount < 0 || amount > self.limits.work - self.work {
self.error("swimlane/layout-limit", "Swimlane geometry work limit exceeded")
return false
}
self.work += amount
true
}
///|
/// Canonical coordinates have three fractional digits and never use exponent notation.
pub fn svg_number(value : Double) -> String {
let scaled = (value * 1000.0).round().to_int64()
if scaled == 0L {
return "0"
}
let negative = scaled < 0L
let magnitude = if negative { -scaled } else { scaled }
let whole = magnitude / 1000L
let fraction = (magnitude % 1000L).to_int()
let prefix = if negative { "-" } else { "" }
if fraction == 0 {
return "\{prefix}\{whole}"
}
let digits = "\{if fraction < 100 { "0" } else { "" }}\{if fraction < 10 { "0" } else { "" }}\{fraction}"
"\{prefix}\{whole}." + digits.trim_end(chars="0").to_owned()
}
///|
/// An inert plain-text projection. Source HTML is never inserted into SVG.
pub fn label_text(text : String) -> String {
let normalized = plain_text(text)
let out = StringBuilder()
let mut cursor = 0
let mut chunk = 0
fn letter(n : Int32) -> Bool {
(n >= 65 && n <= 90) || (n >= 97 && n <= 122)
}
while cursor < normalized.length() {
if normalized.code_unit_at(cursor).to_int() != 60 {
cursor += 1
continue
}
if normalized.view(start_offset=cursor).has_prefix("") is Some(relative) {
out.write_string(
normalized.view(start_offset=chunk, end_offset=cursor).to_owned(),
)
cursor += 4 + relative + 3
chunk = cursor
continue
} else {
cursor = normalized.length()
continue
}
}
let mut name = cursor + 1
if name < normalized.length() &&
normalized.code_unit_at(name).to_int() == 47 {
name += 1
}
if name >= normalized.length() ||
(
!letter(normalized.code_unit_at(name).to_int()) &&
!normalized.view(start_offset=cursor).has_prefix("