use rusttype::Scale;
use crate::{
collision::CollisionStrategy, geometry::Rectangle, placement::PlacementStrategy,
visualization::Canvas, words::FontProvider, words::WordsProvider,
};
pub struct CloudBuilder {
viewport: Rectangle,
listeners: Vec<Box<dyn CanvasListener>>,
}
impl CloudBuilder {
pub fn new(viewport: Rectangle) -> CloudBuilder {
CloudBuilder {
viewport,
listeners: vec![],
}
}
pub fn add_listener<T: CanvasListener + 'static>(&mut self, listener: T) {
self.listeners.push(Box::new(listener));
}
fn trigger_updated(&self, canvas: &Canvas) {
for l in self.listeners.iter() {
l.canvas_updated(canvas);
}
}
pub fn run<WP, PS, CS>(
&self,
provider: &mut WP,
mut placer: PS,
mut collider: CS,
font_provider: &FontProvider,
) -> Canvas
where
WP: WordsProvider,
PS: PlacementStrategy,
CS: CollisionStrategy,
{
let mut canvas = Canvas::new();
while let Some(word) = provider.get_next_word() {
let mut place_found = false;
let mut pos = None;
let Ok(font) = font_provider.get_font(&word.font_name) else {
panic!("Font not found: {}", word.font_name);
};
let r = font.get_bounding_box(&word, Scale::uniform(word.size as f32));
while !place_found && self.viewport.collides(&r) {
pos = placer.find_next_place(pos, &r);
let Some(upos) = pos else { break };
if !collider.collides(upos, &r) {
if cfg!(debug_assertions) {
println!(
"Place found for word '{}': [{}, {}]",
word.text, upos.0, upos.1
);
}
place_found = true;
canvas.add(&word.displace(upos));
collider.add(upos, r.clone());
self.trigger_updated(&canvas);
}
}
if cfg!(debug_assertions) && !place_found {
println!("No place found for word '{}'", word.text);
}
}
canvas
}
}
pub trait CanvasListener {
fn canvas_updated(&self, canvas: &Canvas);
}