// The backend-neutral scenegraph: a tree of marks, each holding items whose
// properties mirror vega-scenegraph (x, y, width, fill, stroke, text, ...).
// Group items contain further marks.
///|
pub(all) enum MarkType {
Group
Rect
Rule
Text
Symbol
Line
Area
}
///|
pub fn MarkType::name(self : MarkType) -> String {
match self {
Group => "group"
Rect => "rect"
Rule => "rule"
Text => "text"
Symbol => "symbol"
Line => "line"
Area => "area"
}
}
///|
pub fn MarkType::from_name(name : String) -> MarkType? {
match name {
"group" => Some(Group)
"rect" => Some(Rect)
"rule" => Some(Rule)
"text" => Some(Text)
"symbol" => Some(Symbol)
"line" => Some(Line)
"area" => Some(Area)
_ => None
}
}
///|
/// Line and area marks render all their items as one path.
pub fn MarkType::is_nested(self : MarkType) -> Bool {
self is Line || self is Area
}
///|
pub(all) struct Item {
/// scene properties by name (`x`, `y`, `fill`, `fontSize`, ...)
props : Map[String, @value.Value]
bounds : Bounds
/// child marks (group items only)
children : Array[Mark]
}
///|
pub(all) struct Mark {
marktype : MarkType
name : String?
role : String?
interactive : Bool
clip : Bool
zindex : Int32?
/// false hides the mark from assistive technology (`aria-hidden`)
aria : Bool
items : Array[Item]
bounds : Bounds
}
///|
pub fn Item::new(props~ : Map[String, @value.Value] = Map([])) -> Item {
{ props, bounds: Bounds::new(), children: [], }
}
///|
pub fn Mark::new(
marktype : MarkType,
name? : String,
role? : String,
interactive~ : Bool = true,
clip~ : Bool = false,
zindex? : Int32,
aria~ : Bool = true,
items~ : Array[Item] = [],
) -> Mark {
{
marktype,
name,
role,
interactive,
clip,
zindex,
aria,
items,
bounds: Bounds::new(),
}
}
// ---- property access with JavaScript semantics ----
///|
pub fn Item::get(self : Item, key : String) -> @value.Value {
match self.props.get(key) {
Some(v) => v
None => Undef
}
}
///|
pub fn Item::set(self : Item, key : String, v : @value.Value) -> Unit {
self.props[key] = v
}
///|
pub fn Item::set_num(self : Item, key : String, x : Double) -> Unit {
self.props[key] = Num(x)
}
///|
pub fn Item::set_str(self : Item, key : String, s : String) -> Unit {
self.props[key] = Str(s)
}
///|
/// `item[key] || 0`
pub fn Item::num0(self : Item, key : String) -> Double {
let v = self.get(key)
if v.truthy() {
v.to_number()
} else {
0.0
}
}
///|
/// `item[key] != null ? +item[key] : default`
pub fn Item::num_or(self : Item, key : String, default : Double) -> Double {
match self.get(key) {
Null | Undef => default
v => v.to_number()
}
}
///|
/// The value when present and not null, as a number.
pub fn Item::opt_num(self : Item, key : String) -> Double? {
match self.get(key) {
Null | Undef => None
v => Some(v.to_number())
}
}
///|
/// A string property, None when missing or null.
pub fn Item::str(self : Item, key : String) -> String? {
match self.get(key) {
Null | Undef => None
Str(s) => Some(s)
v => Some(v.to_js_string())
}
}
///|
/// JavaScript truthiness of a property.
pub fn Item::truthy(self : Item, key : String) -> Bool {
self.get(key).truthy()
}
///|
pub fn Item::has(self : Item, key : String) -> Bool {
match self.get(key) {
Null | Undef => false
_ => true
}
}