1#![feature(int_to_from_bytes, decl_macro, non_ascii_idents)]
2
3pub use minifb;
4pub use rusttype;
5pub mod meter;
9pub mod wu;
10pub mod image;
11pub mod vg;
12pub mod clrs;
13pub mod geom;
14
15use minifb::{Window, MouseMode};
16use rusttype::{point, Scale};
17
18use self::image::{Rectangle, RGBA};
19
20pub use minifb::{Key, MouseButton, CursorStyle};
21pub use rusttype::Font;
22
23pub type Point = (isize, isize);
24
25pub struct Canvas {
26 buffer: Vec<u32>,
27 window: Window,
28 size: (usize, usize),
29}
30
31impl std::ops::Deref for Canvas {
32 type Target = [u32];
33 fn deref(&self) -> &[u32] { &self.buffer }
34}
35
36impl std::ops::DerefMut for Canvas {
37 fn deref_mut(&mut self) -> &mut [u32] { &mut self.buffer }
38}
39
40impl Canvas {
41 pub fn new(title: &str, width: usize, height: usize) -> minifb::Result<Self> {
42 let buffer: Vec<u32> = vec![0; width * height];
43 let window = Window::new(title, width, height, Default::default())?;
44
45 Ok(Self { buffer, window, size: (width, height) })
46 }
47
48 pub fn window(&self) -> &Window { &self.window }
49 pub fn window_mut(&mut self) -> &mut Window { &mut self.window }
50 pub fn buffer(&self) -> &[u32] { &self.buffer }
51 pub fn buffer_mut(&mut self) -> &mut [u32] { &mut self.buffer }
52
53 pub fn image_mut(&mut self) -> RGBA {
54 let (w, h) = self.size;
55 let r = Rectangle::from_size(w as isize, h as isize);
56 RGBA::from_buf32(&mut self.buffer, r)
57 }
58
59 pub fn size(&self) -> (usize, usize) { self.size }
60
61 pub fn is_open(&self) -> bool { self.window.is_open() }
62 pub fn is_keydown(&self, key: Key) -> bool { self.window.is_key_down(key) }
63
64 pub fn set_cursor_style(&mut self, cursor: CursorStyle) {
65 self.window.set_cursor_style(cursor)
66 }
67
68 pub fn keys<F: FnMut(Key)>(&self, f: F) {
69 self.window.get_keys()
70 .map(|mut keys| keys.drain(..).for_each(f));
71 }
72
73 pub fn mouse_pos(&self) -> Option<(f32, f32)> {
74 self.window.get_mouse_pos(MouseMode::Pass)
75 }
76
77 pub fn mouse_down(&self, button: MouseButton) -> bool {
78 self.window.get_mouse_down(button)
79 }
80
81 pub fn mouse_wheel(&self) -> Option<(f32, f32)> {
82 self.window.get_scroll_wheel()
83 }
84
85 pub fn udpate(&mut self) {
86 self.window.update()
87 }
88
89 pub fn redraw(&mut self) -> minifb::Result<()> {
90 self.window.update_with_buffer(&self.buffer)
91 }
92
93 pub fn clear(&mut self) {
94 self.fill(0);
95 }
96
97 pub fn fill(&mut self, color: u32) {
98 self.buffer.iter_mut().for_each(|i| *i = color);
99 }
100
101 pub fn pixel_mut(&mut self, x: usize, y: usize) -> &mut u32 {
102 let (w, h) = self.size();
103 assert!(x < w && y < h, "{}x{}", x, y);
104 let idx = x + y * w;
105 unsafe { self.buffer.get_unchecked_mut(idx) }
106 }
107
108 pub fn pixel(&mut self, x: usize, y: usize, color: u32) {
109 let (w, h) = self.size();
110 if x < w && y < h {
111 let idx = x + y * w;
112 unsafe { *self.buffer.get_unchecked_mut(idx) = color; }
113 }
114 }
115
116 pub fn line(&mut self, start: Point, end: Point, color: u32) {
117 let (w, h) = self.size();
118 let (w, h) = (w as isize, h as isize);
119 wu::clipped_aaline(start, end, (w, h), |x, y, v| {
120 if x >= 0 && x < w && y >= 0 && y < h {
121 let idx = (x + y * w) as usize;
122 unsafe { self.blend(idx, color, v as f32) }
123 }
124 })
125 }
126
127 pub fn hline(&mut self, x1: isize, x2: isize, y: isize, color: u32) {
128 let (w, h) = self.size();
129 let (w, h) = (w as isize, h as isize);
130
131 if y < 0 || y >= h { return }
132 let x1 = x1.max(0);
133 let x2 = x2.min(w);
134
135 for x in x1..x2 {
136 let idx = (x + y * w) as usize;
137 unsafe { *self.buffer.get_unchecked_mut(idx) = color; }
138 }
139 }
140
141 pub fn vline(&mut self, x: isize, y1: isize, y2: isize, color: u32) {
142 let (w, h) = self.size();
143 let (w, h) = (w as isize, h as isize);
144
145 if x < 0 || x >= w { return }
146 let y1 = y1.max(0);
147 let y2 = y2.min(h);
148
149 for y in y1..y2 {
150 let idx = (x + y * w) as usize;
151 unsafe { *self.buffer.get_unchecked_mut(idx) = color; }
152 }
153 }
154
155 pub fn text(&mut self, font: &Font, scale: f32, pos: (f32, f32), color: u32, text: &str) {
156 let scale = Scale::uniform(scale);
157 let v_metrics = font.v_metrics(scale);
158 let (w, h) = self.size();
159
160 for (line, text) in text.lines().enumerate() {
161 let point = point(pos.0, pos.1 + v_metrics.ascent * (line + 1) as f32);
162 for glyph in font.layout(text, scale, point) {
163 if let Some(bbox) = glyph.pixel_bounding_box() {
164 glyph.draw(|x, y, v| {
165 let x = (x + bbox.min.x as u32) as usize;
166 let y = (y + bbox.min.y as u32) as usize;
167 if v != 0.0 && x < w && y < h {
168 unsafe {
169 self.blend(x + y * w, color, v);
170 }
171 }
172 });
173 }
174 }
175 }
176 }
177
178 pub fn circle(&mut self, pos: Point, radius: usize, color: u32) {
179 const PI2: f32 = std::f32::consts::PI * 2.0;
180 let nsamples = 16;
181 let fsamples = nsamples as f32;
182
183 let radius = radius as f32;
184 let pos = (pos.0 as f32, pos.1 as f32);
185
186 self.curve(color, true, (0..nsamples).map(|i| {
187 let (ax, ay) = ((i as f32) / fsamples * PI2).sin_cos();
188 let (ax, ay) = (pos.0 + ax * radius, pos.1 + ay * radius);
189 (ax as isize, ay as isize)
190 }));
191 }
192
193 pub fn curve<I: IntoIterator<Item=Point>>(&mut self, color: u32, loopped: bool, pts: I) {
194 let mut pts = pts.into_iter();
195 let first = if let Some(p) = pts.next() { p } else { return };
196
197 let mut base = first;
198 for p in pts {
199 self.line(base, p, color);
200 base = p;
201 }
202
203 if loopped {
204 self.line(base, first, color);
205 }
206 }
207
208 unsafe fn blend(&mut self, idx: usize, color: u32, alpha: f32) {
209 const MAX_T: f32 = 255.0;
211
212 let pixel = self.buffer.get_unchecked_mut(idx);
213
214 let [db, dg, dr, _] = pixel.to_le_bytes();
215 let [sb, sg, sr, _] = color.to_le_bytes();
216 let (dr, dg, db) = (
217 dr as f32 / MAX_T,
218 dg as f32 / MAX_T,
219 db as f32 / MAX_T,
220 );
221 let (sr, sg, sb) = (
222 sr as f32 / MAX_T,
223 sg as f32 / MAX_T,
224 sb as f32 / MAX_T,
225 );
226
227 let inv_alpha = 1.0 - alpha;
228 let (r, g, b) = (
229 ((sr * alpha + dr * inv_alpha) * MAX_T) as u8,
230 ((sg * alpha + dg * inv_alpha) * MAX_T) as u8,
231 ((sb * alpha + db * inv_alpha) * MAX_T) as u8,
232 );
233
234 *pixel = u32::from_le_bytes([b, g, r, 0xFF]);
236 }
237}