1use neco_view2d::View2d;
2use wasm_bindgen::prelude::*;
3
4#[wasm_bindgen]
5pub struct WasmView2d {
6 inner: View2d,
7}
8
9impl Default for WasmView2d {
10 fn default() -> Self {
11 Self::new()
12 }
13}
14
15#[wasm_bindgen]
16impl WasmView2d {
17 #[wasm_bindgen(constructor)]
18 pub fn new() -> Self {
19 Self {
20 inner: View2d::default(),
21 }
22 }
23
24 pub fn pan(&mut self, dx: f64, dy: f64, canvas_height: f64) {
25 self.inner.pan(dx, dy, canvas_height);
26 }
27
28 pub fn zoom_at(&mut self, delta: f64, cx: f64, cy: f64, cw: f64, ch: f64) {
29 self.inner.zoom_at(delta, cx, cy, cw, ch);
30 }
31
32 pub fn canvas_to_world(&self, cx: f64, cy: f64, cw: f64, ch: f64) -> Vec<f64> {
33 let (wx, wy) = self.inner.canvas_to_world(cx, cy, cw, ch);
34 vec![wx, wy]
35 }
36
37 pub fn world_to_canvas(&self, wx: f64, wy: f64, cw: f64, ch: f64) -> Vec<f64> {
38 let (cx, cy) = self.inner.world_to_canvas(wx, wy, cw, ch);
39 vec![cx, cy]
40 }
41
42 pub fn get_state(&self) -> Vec<f64> {
43 vec![
44 self.inner.center_x,
45 self.inner.center_y,
46 self.inner.view_size,
47 ]
48 }
49
50 pub fn set_state(&mut self, cx: f64, cy: f64, vs: f64) {
51 self.inner.set(cx, cy, vs);
52 }
53
54 pub fn fit(&mut self, ww: f64, wh: f64, cw: f64, ch: f64) {
55 self.inner.fit(ww, wh, cw, ch);
56 }
57
58 pub fn zoom_factor(&self, reference_view_size: f64) -> f64 {
59 self.inner.zoom_factor(reference_view_size)
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use neco_view2d::View2d;
67
68 const EPS: f64 = 1e-10;
69
70 fn assert_vec2_close(actual: Vec<f64>, expected: (f64, f64)) {
71 assert_eq!(actual.len(), 2);
72 assert!((actual[0] - expected.0).abs() < EPS);
73 assert!((actual[1] - expected.1).abs() < EPS);
74 }
75
76 fn assert_vec3_close(actual: Vec<f64>, expected: (f64, f64, f64)) {
77 assert_eq!(actual.len(), 3);
78 assert!((actual[0] - expected.0).abs() < EPS);
79 assert!((actual[1] - expected.1).abs() < EPS);
80 assert!((actual[2] - expected.2).abs() < EPS);
81 }
82
83 #[test]
84 fn new_and_get_state_match_view2d() {
85 let wrapper = WasmView2d::new();
86 let core = View2d::default();
87
88 assert_vec3_close(
89 wrapper.get_state(),
90 (core.center_x, core.center_y, core.view_size),
91 );
92 }
93
94 #[test]
95 fn set_state_pan_zoom_fit_and_zoom_factor_delegate_to_view2d() {
96 let mut wrapper = WasmView2d::new();
97 let mut core = View2d::default();
98
99 wrapper.set_state(30.0, -12.5, 8.0);
100 core.set(30.0, -12.5, 8.0);
101 assert_vec3_close(
102 wrapper.get_state(),
103 (core.center_x, core.center_y, core.view_size),
104 );
105
106 wrapper.pan(5.0, -3.0, 720.0);
107 core.pan(5.0, -3.0, 720.0);
108 assert_vec3_close(
109 wrapper.get_state(),
110 (core.center_x, core.center_y, core.view_size),
111 );
112
113 wrapper.zoom_at(120.0, 320.0, 240.0, 1280.0, 720.0);
114 core.zoom_at(120.0, 320.0, 240.0, 1280.0, 720.0);
115 assert_vec3_close(
116 wrapper.get_state(),
117 (core.center_x, core.center_y, core.view_size),
118 );
119
120 wrapper.fit(1920.0, 1080.0, 800.0, 600.0);
121 core.fit(1920.0, 1080.0, 800.0, 600.0);
122 assert_vec3_close(
123 wrapper.get_state(),
124 (core.center_x, core.center_y, core.view_size),
125 );
126
127 let wrapper_zoom = wrapper.zoom_factor(64.0);
128 let core_zoom = core.zoom_factor(64.0);
129 assert!((wrapper_zoom - core_zoom).abs() < EPS);
130 }
131
132 #[test]
133 fn coordinate_conversions_return_fixed_vec_shapes_and_match_view2d() {
134 let wrapper = WasmView2d::new();
135 let core = View2d::default();
136
137 let wrapper_world = wrapper.canvas_to_world(200.0, 150.0, 800.0, 600.0);
138 let core_world = core.canvas_to_world(200.0, 150.0, 800.0, 600.0);
139 assert_vec2_close(wrapper_world, core_world);
140
141 let wrapper_canvas = wrapper.world_to_canvas(0.25, -0.5, 800.0, 600.0);
142 let core_canvas = core.world_to_canvas(0.25, -0.5, 800.0, 600.0);
143 assert_vec2_close(wrapper_canvas, core_canvas);
144 }
145}