word_cloud/
cloud_builder.rs1use rusttype::Scale;
2
3use crate::{
4 collision::CollisionStrategy, geometry::Rectangle, placement::PlacementStrategy,
5 visualization::Canvas, words::FontProvider, words::WordsProvider,
6};
7
8pub struct CloudBuilder {
10 viewport: Rectangle,
11 listeners: Vec<Box<dyn CanvasListener>>,
12}
13
14impl CloudBuilder {
15 pub fn new(viewport: Rectangle) -> CloudBuilder {
16 CloudBuilder {
17 viewport,
18 listeners: vec![],
19 }
20 }
21
22 pub fn add_listener<T: CanvasListener + 'static>(&mut self, listener: T) {
23 self.listeners.push(Box::new(listener));
24 }
25
26 fn trigger_updated(&self, canvas: &Canvas) {
27 for l in self.listeners.iter() {
28 l.canvas_updated(canvas);
29 }
30 }
31
32 pub fn run<WP, PS, CS>(
33 &self,
34 provider: &mut WP,
35 mut placer: PS,
36 mut collider: CS,
37 font_provider: &FontProvider,
38 ) -> Canvas
39 where
40 WP: WordsProvider,
41 PS: PlacementStrategy,
42 CS: CollisionStrategy,
43 {
44 let mut canvas = Canvas::new();
45
46 while let Some(word) = provider.get_next_word() {
47 let mut place_found = false;
48 let mut pos = None;
49 let Ok(font) = font_provider.get_font(&word.font_name) else {
50 panic!("Font not found: {}", word.font_name);
51 };
52
53 let r = font.get_bounding_box(&word, Scale::uniform(word.size as f32));
54
55 while !place_found && self.viewport.collides(&r) {
56 pos = placer.find_next_place(pos, &r);
57
58 let Some(upos) = pos else { break };
59
60 if !collider.collides(upos, &r) {
61 if cfg!(debug_assertions) {
62 println!(
63 "Place found for word '{}': [{}, {}]",
64 word.text, upos.0, upos.1
65 );
66 }
67 place_found = true;
68 canvas.add(&word.displace(upos));
69 collider.add(upos, r.clone());
70
71 self.trigger_updated(&canvas);
72 }
73 }
74
75 if cfg!(debug_assertions) && !place_found {
76 println!("No place found for word '{}'", word.text);
77 }
78 }
79
80 canvas
81 }
82}
83
84pub trait CanvasListener {
85 fn canvas_updated(&self, canvas: &Canvas);
86}