1use wasm_bindgen::prelude::*;
12use std::error::Error;
13use serde_wasm_bindgen;
14use serde::{Serialize, Deserialize};
15use wasm_bindgen::JsValue;
16use web_sys::{window, Document, HtmlCanvasElement, CanvasRenderingContext2d};
17use web_sys::console;
18use std::rc::Rc;
19use std::cell::RefCell;
20
21
22pub fn draw_responsive_heatmap(
23 context: &CanvasRenderingContext2d,
24 values: Vec<Vec<i32>>,
25 x_labels: Vec<String>,
26 y_labels: Vec<String>,
27 canvas_width: f64,
28 canvas_height: f64,
29 device_pixel_ratio: f64,
30) {
31 let rows = values.len();
32 let cols = values[0].len();
33 console::log_1(&JsValue::from_str(&format!("up in the draw function")));
34 let adj_canvas_width = canvas_width * device_pixel_ratio;
37 let adj_canvas_height = canvas_height * device_pixel_ratio;
38 let padding_left = adj_canvas_width * 0.05;
39 let padding_top = adj_canvas_height * 0.05;
40 let padding_bottom = adj_canvas_height * 0.05;
41 let padding_right = adj_canvas_width * 0.05;
42
43 let box_width = 30.0;
47 let box_height = 30.0;
48 console::log_1(&JsValue::from_str(&format!("pad left {} pad bottom {}",&padding_left, &padding_bottom)));
50 context.clear_rect(0.0, 0.0, adj_canvas_width, adj_canvas_height);
51 println!("cleared rec");
52 for row in 0..rows {
54 for col in 0..cols {
55 let value = values[row][col];
56
57 let color = match value {
59 0 => "#fee0d2",
60 1 => "#fc9272",
61 2 => "#de2d26",
62 _ => "#FFFFFF",
63 };
64 context.set_fill_style(&JsValue::from_str(color));
65
66 let x = padding_left + (col as f64 * box_width);
67 let y = padding_top + (row as f64 * box_height);
68 context.fill_rect(x, y, box_width, box_height);
69
70 context.set_stroke_style(&JsValue::from_str("#FFFFFF"));
72 context.set_line_width(2.0 / device_pixel_ratio);
73
74 if row < rows - 1 {
75 context.begin_path();
76 context.move_to(x, y + box_height);
77 context.line_to(x + box_width, y + box_height);
78 context.stroke();
79 }
80
81 if col < cols - 1 {
82 context.begin_path();
83 context.move_to(x + box_width, y);
84 context.line_to(x + box_width, y + box_height);
85 context.stroke();
86 }
87 }
88 }
89 console::log_1(&JsValue::from_str(&format!(
90 "after the rows and cols padding bottom: {}, height: {}",
91 &padding_bottom,
92 &(box_height * rows as f64),
93 )));
94
95 context.begin_path();
97 context.set_stroke_style(&JsValue::from_str("#000000"));
98 context.move_to(padding_left, (box_height * rows as f64) + padding_bottom);
99 context.line_to((box_height * rows as f64) + padding_bottom, (box_height * rows as f64) + padding_left);
100 context.stroke();
101
102 context.begin_path();
104 context.move_to(padding_left, padding_top);
105 context.line_to(padding_left, (box_height * rows as f64) + padding_bottom);
106 context.stroke();
107
108 let label_font_size = (box_height * 0.3).min(box_width * 0.3).max(12.0);
110 context.set_font(&format!("{}px Arial", label_font_size));
111 context.set_text_align("center");
112 context.set_text_baseline("top");
113
114 for col in 0..cols {
115 let x = padding_left + col as f64 * box_width + box_width / 2.0;
116 let y = (box_height * rows as f64) + padding_bottom + 5.0; context.fill_text(&x_labels[col], x, y).unwrap();
118
119 context.begin_path();
121 context.move_to(x, (box_height * rows as f64) + padding_bottom);
122 context.line_to(x, (box_height * rows as f64) + padding_bottom + 5.0);
123 context.stroke();
124 }
125
126 context.set_text_align("right");
128 context.set_text_baseline("middle");
129
130 for row in 0..rows {
131 let x = padding_left - 10.0; let y = padding_top + row as f64 * box_height + box_height / 2.0;
133 context.fill_text(&y_labels[row], x, y).unwrap();
134
135 context.begin_path();
137 context.move_to(padding_left, y);
138 context.line_to(padding_left - 5.0, y);
139 context.stroke();
140 }
141 console::log_1(&JsValue::from_str(&format!(
142 "at the end of draw funct Canvas width: {}, height: {}",
143 &adj_canvas_width,
144 &adj_canvas_height
145 )));
146}
147
148#[derive(Serialize, Deserialize, Clone, Debug)]
149pub struct HeatmapData {
150 values: Vec<Vec<i32>>,
151 x_labels: Vec<String>,
152 y_labels: Vec<String>,
153}
154
155impl HeatmapData {
156 pub fn new() -> Self {
158 HeatmapData {
159 values: vec![vec![0]],
160 x_labels: Vec::new(),
161 y_labels: Vec::new(),
162 }
163 }
164}
165
166
167#[wasm_bindgen(start)]
169pub fn start() -> Result<(), JsValue> {
170 console::log_1(&JsValue::from_str(&format!("literal start")));
172 let window = window().expect("should have a window in this context");
173 let window = Rc::new(window);
174 let window_clone = Rc::clone(&window);
175 let document = window.document().expect("should have a document on window");
176 console::log_1(&JsValue::from_str(&format!("up in the start of the function")));
177 let canvas = document
179 .get_element_by_id("heatmap")
180 .expect("Canvas element not found")
181 .dyn_into::<HtmlCanvasElement>()?;
182 console::log_1(&JsValue::from_str(&format!("called the canvas")));
183 let heatmap_values = vec![
184 vec![2, 1, 0, 1, 0], vec![1, 2, 0, 0, 1], vec![2, 0, 1, 2, 1], vec![0, 0, 0, 2, 0], vec![1, 2, 0, 1, 1], ];
190 console::log_1(&JsValue::from_str(&format!("called the heatmap vals")));
191 let x_labels: Vec<String> = vec!["A", "B", "C", "D", "E"].iter().map(|s| s.to_string()).collect();
192 let y_labels: Vec<String> = vec!["R1", "R2", "R3", "R4", "R5"].iter().map(|s| s.to_string()).collect();
193
194 let num_rows = heatmap_values.len(); let num_cols = heatmap_values[0].len(); let mut heatmap_data = HeatmapData::new();
197 heatmap_data = HeatmapData { values: heatmap_values.clone(), x_labels: x_labels.clone(), y_labels: y_labels.clone() };
198 let box_size = 100.0;
199 let device_pixel_ratio = window.device_pixel_ratio();
200 console::log_1(&JsValue::from_str(&format!("num rows are {:?} num cols are {:?}", &num_rows, &num_cols)));
201
202 let canvas_width = num_cols as f64 * box_size; let canvas_height = num_rows as f64 * box_size; canvas.set_width(canvas_width as u32);
206 canvas.set_height(canvas_height as u32);
207 console::log_1(&JsValue::from_str(&format!(
208 "Canvas width: {}, height: {}",
209 canvas.width(),
210 canvas.height()
211 )));
212
213 let context = canvas
214 .get_context("2d")?
215 .unwrap()
216 .dyn_into::<CanvasRenderingContext2d>()?;
217
218 context.scale(device_pixel_ratio, device_pixel_ratio);
220
221 draw_responsive_heatmap(
222 &context,
223 heatmap_values.clone(),
224 x_labels.clone(),
225 y_labels.clone(),
226 canvas_width,
227 canvas_height,
228 device_pixel_ratio,
229 );
230
231 let closure = Closure::wrap(Box::new(move || {
232 let new_width = window_clone.inner_width().unwrap().as_f64().unwrap();
233 let new_height = window_clone.inner_height().unwrap().as_f64().unwrap();
234
235 let canvas_new_width = (num_cols as f64 * box_size).min(new_width);
237 let canvas_new_height = (num_rows as f64 * box_size).min(new_height);
238
239 canvas.set_width(canvas_new_width as u32);
240 canvas.set_height(canvas_new_height as u32);
241
242 context.set_transform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0).unwrap();
244 context.scale(device_pixel_ratio, device_pixel_ratio).unwrap();
245
246 draw_responsive_heatmap(
247 &context,
248 heatmap_values.clone(),
249 x_labels.clone(),
250 y_labels.clone(),
251 canvas_new_width.into(),
252 canvas_new_height.into(),
253 device_pixel_ratio,
254 );
255 }) as Box<dyn FnMut()>);
256
257 window.add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref()).unwrap();
258 closure.forget();
259
260 Ok(())
261}