// Read-only scene graph queries used by inspectors, selection and layout tools.
///|
pub(all) struct DocumentMetrics {
node_count : Int
root_count : Int
leaf_count : Int
group_count : Int
shape_count : Int
text_count : Int
image_count : Int
artboard_count : Int
visible_count : Int
locked_count : Int
max_depth : Int
total_bounds : @math.Rect2D
total_area : Double
} derive(Eq, Debug)
///|
pub fn ArtboardDocument::metrics(self : ArtboardDocument) -> DocumentMetrics {
let entries = self.traverse()
let mut leaf_count = 0
let mut group_count = 0
let mut shape_count = 0
let mut text_count = 0
let mut image_count = 0
let mut artboard_count = 0
let mut visible_count = 0
let mut locked_count = 0
let mut max_depth = 0
let mut total_bounds = @math.Rect2D::empty()
for i = 0; i < entries.length(); i = i + 1 {
let entry = entries[i]
if entry.depth > max_depth {
max_depth = entry.depth
}
total_bounds = total_bounds.union(entry.world_bounds)
match self.nodes.get(entry.id.value) {
None => ()
Some(node) => {
if node.visible {
visible_count = visible_count + 1
}
if node.locked {
locked_count = locked_count + 1
}
match node.node_type {
GroupNode(_) => group_count = group_count + 1
ShapeNode(_) => {
shape_count = shape_count + 1
leaf_count = leaf_count + 1
}
TextNode(_) => {
text_count = text_count + 1
leaf_count = leaf_count + 1
}
ImageNode(_) => {
image_count = image_count + 1
leaf_count = leaf_count + 1
}
ArtboardNode(_) => {
artboard_count = artboard_count + 1
leaf_count = leaf_count + 1
}
}
}
}
}
{
node_count: entries.length(),
root_count: self.root_ids.length(),
leaf_count,
group_count,
shape_count,
text_count,
image_count,
artboard_count,
visible_count,
locked_count,
max_depth,
total_bounds,
total_area: total_bounds.width() * total_bounds.height(),
}
}
///|
pub fn ArtboardDocument::ordered_node_ids(
self : ArtboardDocument,
) -> Array[NodeID] {
let entries = self.traverse()
let result : Array[NodeID] = []
for i = 0; i < entries.length(); i = i + 1 {
result.push(entries[i].id)
}
result
}
///|
pub fn ArtboardDocument::leaf_node_ids(
self : ArtboardDocument,
) -> Array[NodeID] {
let result : Array[NodeID] = []
let ordered = self.ordered_node_ids()
for i = 0; i < ordered.length(); i = i + 1 {
match self.nodes.get(ordered[i].value) {
Some(node) =>
match node.node_type {
GroupNode(_) => ()
_ => result.push(node.id)
}
None => ()
}
}
result
}
///|
pub fn ArtboardDocument::find_by_name(
self : ArtboardDocument,
name : String,
) -> Array[NodeID] {
let result : Array[NodeID] = []
let ordered = self.ordered_node_ids()
for i = 0; i < ordered.length(); i = i + 1 {
match self.nodes.get(ordered[i].value) {
Some(node) if node.name == name => result.push(node.id)
_ => ()
}
}
result
}
///|
pub fn ArtboardDocument::find_name_contains(
self : ArtboardDocument,
fragment : String,
) -> Array[NodeID] {
let result : Array[NodeID] = []
let ordered = self.ordered_node_ids()
for i = 0; i < ordered.length(); i = i + 1 {
match self.nodes.get(ordered[i].value) {
Some(node) if node.name.contains(fragment) => result.push(node.id)
_ => ()
}
}
result
}
///|
pub fn ArtboardDocument::descendants(
self : ArtboardDocument,
id : NodeID,
) -> Array[NodeID] {
let result : Array[NodeID] = []
let pending : Array[NodeID] = []
let visited : Map[String, Bool] = Map([])
pending.push(id)
while !pending.is_empty() {
let current = pending.unsafe_pop()
match visited.get(current.value) {
Some(_) => ()
None => {
visited[current.value] = true
match self.nodes.get(current.value) {
Some(node) =>
match node.node_type {
GroupNode(children) =>
for i = children.length() - 1; i >= 0; i = i - 1 {
let child = children[i]
result.push(child)
pending.push(child)
}
_ => ()
}
None => ()
}
}
}
}
result
}
///|
pub fn ArtboardDocument::ancestors(
self : ArtboardDocument,
id : NodeID,
) -> Array[NodeID] {
let result : Array[NodeID] = []
let visited : Map[String, Bool] = Map([])
let mut current = id
while true {
match self.nodes.get(current.value) {
None => break
Some(node) =>
match node.parent_id {
None => break
Some(parent) =>
match visited.get(parent.value) {
Some(_) => break
None => {
visited[parent.value] = true
result.push(parent)
current = parent
}
}
}
}
}
result
}
///|
pub fn ArtboardDocument::is_ancestor(
self : ArtboardDocument,
ancestor : NodeID,
descendant : NodeID,
) -> Bool {
self.ancestors(descendant).contains(ancestor)
}
///|
pub fn ArtboardDocument::depth_of(self : ArtboardDocument, id : NodeID) -> Int {
let entries = self.traverse()
for i = 0; i < entries.length(); i = i + 1 {
if entries[i].id == id {
return entries[i].depth
}
}
-1
}
///|
pub fn ArtboardDocument::nodes_intersecting(
self : ArtboardDocument,
rect : @math.Rect2D,
) -> Array[NodeID] {
let result : Array[NodeID] = []
let entries = self.traverse()
for i = 0; i < entries.length(); i = i + 1 {
if entries[i].world_bounds.intersects(rect) {
result.push(entries[i].id)
}
}
result
}
///|
pub fn ArtboardDocument::visible_bounds(
self : ArtboardDocument,
) -> @math.Rect2D {
let mut result = @math.Rect2D::empty()
let entries = self.traverse()
for i = 0; i < entries.length(); i = i + 1 {
match self.nodes.get(entries[i].id.value) {
Some(node) if node.visible =>
result = result.union(entries[i].world_bounds)
_ => ()
}
}
result
}
///|
pub fn ArtboardDocument::node_type_name(
self : ArtboardDocument,
id : NodeID,
) -> String? {
match self.nodes.get(id.value) {
None => None
Some(node) =>
Some(
match node.node_type {
ArtboardNode(_) => "artboard"
GroupNode(_) => "group"
ShapeNode(_) => "shape"
TextNode(_) => "text"
ImageNode(_) => "image"
},
)
}
}
///|
pub fn ArtboardDocument::count_visible(self : ArtboardDocument) -> Int {
self.metrics().visible_count
}
///|
pub fn ArtboardDocument::count_locked(self : ArtboardDocument) -> Int {
self.metrics().locked_count
}