use std::collections::HashMap;
use macroquad::prelude::{Color, Vec2};
use crate::primitives::{Entity, FontKind, Shape, StrokeStyle};
use crate::style;
#[derive(Debug, Clone, Default)]
pub struct Scene {
pub entities: Vec<Entity>,
index: HashMap<String, usize>,
}
impl Scene {
pub fn new() -> Self {
Scene::default()
}
pub fn add(&mut self, e: Entity) -> usize {
assert!(
!self.index.contains_key(&e.id),
"duplicate entity id {:?}",
e.id
);
let i = self.entities.len();
self.index.insert(e.id.clone(), i);
self.entities.push(e);
i
}
pub fn get(&self, id: &str) -> Option<&Entity> {
self.index.get(id).map(|&i| &self.entities[i])
}
pub fn get_mut(&mut self, id: &str) -> Option<&mut Entity> {
self.index.get(id).copied().map(move |i| &mut self.entities[i])
}
pub fn contains(&self, id: &str) -> bool {
self.index.contains_key(id)
}
}
pub struct SceneBuilder<'a> {
scene: &'a mut Scene,
last: Option<usize>,
}
impl<'a> SceneBuilder<'a> {
pub(crate) fn new(scene: &'a mut Scene) -> Self {
SceneBuilder { scene, last: None }
}
fn push(&mut self, e: Entity) -> &mut Self {
self.last = Some(self.scene.add(e));
self
}
fn last_mut(&mut self) -> &mut Entity {
let i = self.last.expect("modifier called before any shape was added");
&mut self.scene.entities[i]
}
pub fn circle(&mut self, id: &str, pos: Vec2, r: f32) -> &mut Self {
let mut e = Entity::new(id, Shape::Circle { r }, pos, style::PAPER);
e.stroke = StrokeStyle { fill: true, outline: true, outline_color: Some(style::INK), ..Default::default() };
self.push(e)
}
pub fn rect(&mut self, id: &str, pos: Vec2, w: f32, h: f32) -> &mut Self {
let mut e = Entity::new(id, Shape::Rect { w, h }, pos, style::PAPER);
e.stroke = StrokeStyle { fill: true, outline: true, outline_color: Some(style::INK), ..Default::default() };
self.push(e)
}
pub fn line(&mut self, id: &str, from: Vec2, to: Vec2) -> &mut Self {
self.push(Entity::new(id, Shape::Line { to }, from, style::INK))
}
pub fn arrow(&mut self, id: &str, from: Vec2, to: Vec2) -> &mut Self {
self.push(Entity::new(id, Shape::Arrow { to }, from, style::INK))
}
pub fn polygon(&mut self, id: &str, pts: Vec<Vec2>) -> &mut Self {
let mut e = Entity::new(id, Shape::Polygon { pts }, Vec2::ZERO, style::PAPER);
e.stroke = StrokeStyle { fill: true, outline: true, outline_color: Some(style::INK), ..Default::default() };
self.push(e)
}
pub fn text(&mut self, id: &str, pos: Vec2, content: &str) -> &mut Self {
self.push(Entity::new(
id,
Shape::Text { content: content.into(), size: 28.0 },
pos,
style::INK,
))
}
pub fn color(&mut self, c: Color) -> &mut Self {
self.last_mut().color = c;
self
}
pub fn outlined(&mut self) -> &mut Self {
let e = self.last_mut();
e.stroke.fill = false;
e.stroke.outline = true;
self
}
pub fn filled(&mut self) -> &mut Self {
let e = self.last_mut();
e.stroke.fill = true;
e.stroke.outline = false;
self
}
pub fn stroke(&mut self, w: f32) -> &mut Self {
self.last_mut().stroke.width = w;
self
}
pub fn outline_color(&mut self, c: Color) -> &mut Self {
self.last_mut().stroke.outline_color = Some(c);
self
}
pub fn size(&mut self, s: f32) -> &mut Self {
if let Shape::Text { size, .. } = &mut self.last_mut().shape {
*size = s;
}
self
}
pub fn serif(&mut self) -> &mut Self {
self.last_mut().font = FontKind::Serif;
self
}
pub fn mono_bold(&mut self) -> &mut Self {
self.last_mut().font = FontKind::MonoBold;
self
}
pub fn z(&mut self, z: i32) -> &mut Self {
self.last_mut().z = z;
self
}
pub fn hidden(&mut self) -> &mut Self {
self.last_mut().opacity = 0.0;
self
}
pub fn opacity(&mut self, o: f32) -> &mut Self {
self.last_mut().opacity = o;
self
}
pub fn rot(&mut self, deg: f32) -> &mut Self {
self.last_mut().rot = deg;
self
}
pub fn wrap(&mut self, px: f32) -> &mut Self {
self.last_mut().wrap = Some(px);
self
}
pub fn follow(&mut self, id: &str, offset: Vec2) -> &mut Self {
self.last_mut().follow = Some((id.into(), offset));
self
}
pub fn label(&mut self, text: &str) -> &mut Self {
let (parent_id, parent_z) = {
let e = self.last_mut();
(e.id.clone(), e.z)
};
let mut lbl = Entity::new(
format!("{parent_id}.label"),
Shape::Text { content: text.into(), size: 24.0 },
Vec2::ZERO,
style::INK,
);
lbl.font = FontKind::MonoBold;
lbl.z = parent_z + 1;
lbl.follow = Some((parent_id, Vec2::ZERO));
self.push(lbl)
}
}