///|
pub(all) struct Point {
x : Double
y : Double
} derive(Eq, Debug)
///|
pub extend Point with Eq::{equal, not_equal}
///|
pub extend Point with @debug.Debug::{to_repr}
///|
pub(all) struct Rect {
x : Double
y : Double
width : Double
height : Double
} derive(Eq, Debug)
///|
pub extend Rect with Eq::{equal, not_equal}
///|
pub extend Rect with @debug.Debug::{to_repr}
///|
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
}
///|
/// Measured text. `x` is the horizontal centre and `box` the text bounds.
pub struct Label {
lines : ReadOnlyArray[String]
x : Double
size : Double
weight : Int32
box : Rect
/// The semantic owner: a term, rule, frame or connector identity.
owner : String
} derive(Eq, Debug)
///|
pub extend Label with Eq::{equal, not_equal}
///|
pub extend Label with @debug.Debug::{to_repr}
///|
/// The visual form of a term.
pub(all) enum Shape {
/// A consuming terminal or literal.
Pill
/// A nonterminal reference.
Square
/// A predefined class or set.
Class
/// A special sequence.
Special
/// A zero-width anchor.
Anchor
/// A lookaround frame. The frame is the visit region.
Lookaround
/// An empty term.
Dot
} derive(Eq, Debug)
///|
pub extend Shape with Eq::{equal, not_equal}
///|
pub extend Shape with @debug.Debug::{to_repr}
///|
pub struct TermBox {
term : @trace.Term
shape : Shape
rect : Rect
label : Label
notes : ReadOnlyArray[Label]
} derive(Eq, Debug)
///|
pub extend TermBox with Eq::{equal, not_equal}
///|
pub extend TermBox with @debug.Debug::{to_repr}
///|
pub(all) enum FrameRole {
/// A capturing, named or non-capturing group.
Group
/// The constraint region of an exclusion.
Except
} derive(Eq, Debug)
///|
pub extend FrameRole with Eq::{equal, not_equal}
///|
pub extend FrameRole with @debug.Debug::{to_repr}
///|
pub struct Frame {
owner : String
role : FrameRole
rect : Rect
label : Label
} derive(Eq, Debug)
///|
pub extend Frame with Eq::{equal, not_equal}
///|
pub extend Frame with @debug.Debug::{to_repr}
///|
/// The drawn path of one connector, as a polyline in its semantic direction.
/// Corners are rounded when painted.
pub struct Wire {
connector : @trace.Connector
points : ReadOnlyArray[Point]
/// A repetition bound or count label of a return path.
label : Label?
} derive(Eq, Debug)
///|
pub extend Wire with Eq::{equal, not_equal}
///|
pub extend Wire with @debug.Debug::{to_repr}
///|
pub struct TrackBox {
track : @trace.Track
label : Label
start : Point
end : Point
notes : ReadOnlyArray[Label]
} derive(Eq, Debug)
///|
pub extend TrackBox with Eq::{equal, not_equal}
///|
pub extend TrackBox with @debug.Debug::{to_repr}
///|
pub struct NoteBox {
id : String
rect : Rect
label : Label
} derive(Eq, Debug)
///|
pub extend NoteBox with Eq::{equal, not_equal}
///|
pub extend NoteBox with @debug.Debug::{to_repr}
///|
/// Deterministic railroad geometry. Every drawn connector of the topology has
/// one wire; every term has one box.
pub struct Geometry {
topology : @trace.Topology
width : Double
height : Double
title : Label?
tracks : ReadOnlyArray[TrackBox]
terms : ReadOnlyArray[TermBox]
frames : ReadOnlyArray[Frame]
wires : ReadOnlyArray[Wire]
notes : ReadOnlyArray[NoteBox]
priv point_index : Map[String, Point]
}
///|
/// The coordinates of a topology point.
pub fn Geometry::point(self : Geometry, id : String) -> Point? {
self.point_index.get(id)
}
///|
/// Make geometry from parts, for tests of the legibility checker.
pub fn Geometry::new(
topology : @trace.Topology,
width : Double,
height : Double,
tracks : ArrayView[TrackBox],
terms : ArrayView[TermBox],
frames : ArrayView[Frame],
wires : ArrayView[Wire],
notes : ArrayView[NoteBox],
points : ArrayView[(String, Point)],
title? : Label,
) -> Geometry {
{
topology,
width,
height,
title,
tracks: ReadOnlyArray::from_array(tracks),
terms: ReadOnlyArray::from_array(terms),
frames: ReadOnlyArray::from_array(frames),
wires: ReadOnlyArray::from_array(wires),
notes: ReadOnlyArray::from_array(notes),
point_index: Map::from_array(points.to_owned()),
}
}
///|
pub fn Geometry::points(self : Geometry) -> Array[(String, Point)] {
self.point_index.iter().collect()
}
///|
pub struct Result {
geometry : Geometry?
diagnostics : ReadOnlyArray[@diagrams.Diagnostic]
}
///|
/// The two railroad source models.
pub(all) enum Model {
Regex(@regex.Regex)
Ebnf(@ebnf.Grammar)
} derive(Eq, Debug)
///|
pub extend Model with Eq::{equal, not_equal}
///|
pub extend Model with @debug.Debug::{to_repr}
///|
/// Format a coordinate for SVG with at most three decimals.
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 mut digits = (fraction + 1000).to_string()
digits = cut(digits, 1, digits.length())
while digits.has_suffix("0") {
digits = cut(digits, 0, digits.length() - 1)
}
"\{prefix}\{whole}.\{digits}"
}
///|
/// The SVG path data of a wire, with rounded corners.
pub fn Wire::path(self : Wire, radius~ : Double = 8.0) -> String {
let p = self.points
let n = svg_number
if p.length() == 0 {
return ""
}
let out = StringBuilder()
out.write_string("M\{n(p[0].x)} \{n(p[0].y)}")
for i in 1.. Double {
let mut total = 0.0
for i in 1.. Wire {
{ ..self, points: ReadOnlyArray::from_array(points), }
}
///|
/// The term with another rectangle, for supplied test geometry.
pub fn TermBox::with_rect(self : TermBox, rect : Rect) -> TermBox {
{ ..self, rect, }
}