use std::collections::BTreeMap;
use zenith_core::{Diagnostic, GroupNode, InstanceNode, Node, Override, PropertyValue};
use crate::ir::SceneCommand;
use super::super::{NodeCtx, RenderCtx};
use super::group::compile_group;
pub(in crate::compile) fn compile_instance(
instance: &InstanceNode,
cx: NodeCtx,
commands: &mut Vec<SceneCommand>,
diagnostics: &mut Vec<Diagnostic>,
connector_strokes: &mut Vec<usize>,
ctx: RenderCtx,
) {
if instance.visible == Some(false) {
return;
}
let Some(component) = cx.components.get(instance.component.as_str()) else {
diagnostics.push(Diagnostic::advisory(
"scene.unknown_component",
format!(
"instance '{}' references component '{}' which is not declared; \
the instance is skipped",
instance.id, instance.component
),
instance.source_span,
Some(instance.id.clone()),
));
return;
};
let mut children = component.children.clone();
for ov in &instance.overrides {
apply_override(&mut children, ov);
}
let prefix = format!("{}/", instance.id);
prefix_ids_in_children(&mut children, &prefix);
let synthetic = GroupNode {
id: instance.id.clone(),
name: instance.name.clone(),
role: instance.role.clone(),
x: instance.x.clone().map(PropertyValue::Dimension),
y: instance.y.clone().map(PropertyValue::Dimension),
w: None,
h: None,
opacity: instance.opacity,
visible: instance.visible,
locked: instance.locked,
rotate: None,
blend_mode: None,
shadow: None,
filter: None,
mask: None,
blur: None,
style: None,
semantic_role: None,
intensity: None,
layer_priority: None,
anchor: None,
anchor_zone: None,
anchor_sibling: None,
anchor_edge: None,
anchor_gap: None,
anchor_parent: None,
children,
protected_regions: Vec::new(),
editable_param_ids: Vec::new(),
source_span: instance.source_span,
unknown_props: BTreeMap::new(),
};
compile_group(
&synthetic,
cx,
commands,
diagnostics,
connector_strokes,
ctx,
);
}
fn apply_override(children: &mut [Node], ov: &Override) -> bool {
for child in children.iter_mut() {
if node_local_id(child) == Some(ov.ref_id.as_str()) {
apply_override_to_node(child, ov);
return true;
}
let grandchildren = match child {
Node::Frame(f) => Some(&mut f.children),
Node::Group(g) => Some(&mut g.children),
Node::Rect(_)
| Node::Ellipse(_)
| Node::Line(_)
| Node::Text(_)
| Node::Code(_)
| Node::Image(_)
| Node::Polygon(_)
| Node::Polyline(_)
| Node::Instance(_)
| Node::Field(_)
| Node::Footnote(_)
| Node::Toc(_)
| Node::Table(_)
| Node::Shape(_)
| Node::Connector(_)
| Node::Pattern(_)
| Node::Chart(_)
| Node::Light(_)
| Node::Mesh(_)
| Node::Unknown(_) => None,
};
if let Some(gc) = grandchildren
&& apply_override(gc, ov)
{
return true;
}
}
false
}
fn apply_override_to_node(node: &mut Node, ov: &Override) {
if let Some(spans) = &ov.spans
&& let Node::Text(t) = node
{
t.spans = spans.clone();
}
if let Some(fill) = &ov.fill {
set_node_fill(node, fill.clone());
}
if let Some(v) = ov.visible {
set_node_visible(node, v);
}
}
fn set_node_fill(node: &mut Node, fill: PropertyValue) {
match node {
Node::Rect(n) => n.fill = Some(fill),
Node::Ellipse(n) => n.fill = Some(fill),
Node::Text(n) => n.fill = Some(fill),
Node::Code(n) => n.fill = Some(fill),
Node::Polygon(n) => n.fill = Some(fill),
Node::Polyline(n) => n.fill = Some(fill),
Node::Field(n) => n.fill = Some(fill),
Node::Toc(n) => n.fill = Some(fill),
Node::Footnote(n) => n.fill = Some(fill),
Node::Table(n) => n.fill = Some(fill),
Node::Shape(n) => n.fill = Some(fill),
Node::Pattern(n) => n.fill = Some(fill),
Node::Chart(n) => n.fill = Some(fill),
Node::Light(_) => {}
Node::Mesh(n) => n.stroke = Some(fill),
Node::Line(_)
| Node::Frame(_)
| Node::Group(_)
| Node::Image(_)
| Node::Instance(_)
| Node::Connector(_)
| Node::Unknown(_) => {}
}
}
fn set_node_visible(node: &mut Node, v: bool) {
match node {
Node::Rect(n) => n.visible = Some(v),
Node::Ellipse(n) => n.visible = Some(v),
Node::Line(n) => n.visible = Some(v),
Node::Text(n) => n.visible = Some(v),
Node::Code(n) => n.visible = Some(v),
Node::Frame(n) => n.visible = Some(v),
Node::Group(n) => n.visible = Some(v),
Node::Image(n) => n.visible = Some(v),
Node::Polygon(n) => n.visible = Some(v),
Node::Polyline(n) => n.visible = Some(v),
Node::Instance(n) => n.visible = Some(v),
Node::Field(n) => n.visible = Some(v),
Node::Toc(n) => n.visible = Some(v),
Node::Table(n) => n.visible = Some(v),
Node::Shape(n) => n.visible = Some(v),
Node::Connector(n) => n.visible = Some(v),
Node::Pattern(n) => n.visible = Some(v),
Node::Chart(n) => n.visible = Some(v),
Node::Light(n) => n.visible = Some(v),
Node::Mesh(n) => n.visible = Some(v),
Node::Footnote(_) => {}
Node::Unknown(_) => {}
}
}
fn node_local_id(node: &Node) -> Option<&str> {
match node {
Node::Rect(n) => Some(&n.id),
Node::Ellipse(n) => Some(&n.id),
Node::Line(n) => Some(&n.id),
Node::Text(n) => Some(&n.id),
Node::Code(n) => Some(&n.id),
Node::Frame(n) => Some(&n.id),
Node::Group(n) => Some(&n.id),
Node::Image(n) => Some(&n.id),
Node::Polygon(n) => Some(&n.id),
Node::Polyline(n) => Some(&n.id),
Node::Instance(n) => Some(&n.id),
Node::Field(n) => Some(&n.id),
Node::Toc(n) => Some(&n.id),
Node::Footnote(n) => Some(&n.id),
Node::Table(n) => Some(&n.id),
Node::Shape(n) => Some(&n.id),
Node::Connector(n) => Some(&n.id),
Node::Pattern(n) => Some(&n.id),
Node::Chart(n) => Some(&n.id),
Node::Light(n) => Some(&n.id),
Node::Mesh(n) => Some(&n.id),
Node::Unknown(_) => None,
}
}
pub(in crate::compile) fn prefix_ids_in_children(children: &mut [Node], prefix: &str) {
for child in children.iter_mut() {
prefix_node_id(child, prefix);
match child {
Node::Frame(f) => prefix_ids_in_children(&mut f.children, prefix),
Node::Group(g) => prefix_ids_in_children(&mut g.children, prefix),
Node::Table(t) => {
for row in &mut t.rows {
for cell in &mut row.cells {
prefix_ids_in_children(&mut cell.children, prefix);
}
}
}
Node::Rect(_)
| Node::Ellipse(_)
| Node::Line(_)
| Node::Text(_)
| Node::Code(_)
| Node::Image(_)
| Node::Polygon(_)
| Node::Polyline(_)
| Node::Instance(_)
| Node::Field(_)
| Node::Footnote(_)
| Node::Toc(_)
| Node::Shape(_)
| Node::Connector(_)
| Node::Pattern(_)
| Node::Chart(_)
| Node::Light(_)
| Node::Mesh(_)
| Node::Unknown(_) => {}
}
}
}
fn prefix_node_id(node: &mut Node, prefix: &str) {
macro_rules! pre {
($field:expr) => {{
$field = format!("{prefix}{}", $field);
}};
}
match node {
Node::Rect(n) => pre!(n.id),
Node::Ellipse(n) => pre!(n.id),
Node::Line(n) => pre!(n.id),
Node::Text(n) => pre!(n.id),
Node::Code(n) => pre!(n.id),
Node::Frame(n) => pre!(n.id),
Node::Group(n) => pre!(n.id),
Node::Image(n) => pre!(n.id),
Node::Polygon(n) => pre!(n.id),
Node::Polyline(n) => pre!(n.id),
Node::Instance(n) => pre!(n.id),
Node::Field(n) => pre!(n.id),
Node::Toc(n) => pre!(n.id),
Node::Footnote(n) => pre!(n.id),
Node::Table(n) => pre!(n.id),
Node::Shape(n) => pre!(n.id),
Node::Connector(n) => pre!(n.id),
Node::Pattern(n) => pre!(n.id),
Node::Chart(n) => pre!(n.id),
Node::Light(n) => pre!(n.id),
Node::Mesh(n) => pre!(n.id),
Node::Unknown(_) => {}
}
}