Showing
14 changed files
with
794 additions
and
793 deletions
src/easel.rs
deleted
100644 → 0
| 1 | -// | ||
| 2 | -// This is an abstraction over a drawing environment. | ||
| 3 | -// Future note: z-Buffer is described here: | ||
| 4 | -// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes | ||
| 5 | -// | ||
| 6 | -// Georg Hopp <georg@steffers.org> | ||
| 7 | -// | ||
| 8 | -// Copyright © 2019 Georg Hopp | ||
| 9 | -// | ||
| 10 | -// This program is free software: you can redistribute it and/or modify | ||
| 11 | -// it under the terms of the GNU General Public License as published by | ||
| 12 | -// the Free Software Foundation, either version 3 of the License, or | ||
| 13 | -// (at your option) any later version. | ||
| 14 | -// | ||
| 15 | -// This program is distributed in the hope that it will be useful, | ||
| 16 | -// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | -// GNU General Public License for more details. | ||
| 19 | -// | ||
| 20 | -// You should have received a copy of the GNU General Public License | ||
| 21 | -// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | -// | ||
| 23 | -use std::cmp; | ||
| 24 | -use std::fmt::{Formatter, Debug, Display, Result}; | ||
| 25 | -use std::ops::{Add, Sub, Div}; | ||
| 26 | -use std::sync::mpsc; | ||
| 27 | - | ||
| 28 | -pub trait Easel { | ||
| 29 | - //fn canvas(&mut self, width :u16, height :u16) -> Option<&dyn Canvas>; | ||
| 30 | -} | ||
| 31 | - | ||
| 32 | -pub trait Canvas<T> { | ||
| 33 | - fn init_events(&self); | ||
| 34 | - fn start_events(&self, tx :mpsc::Sender<i32>); | ||
| 35 | - | ||
| 36 | - fn width(&self) -> u16; | ||
| 37 | - fn height(&self) -> u16; | ||
| 38 | - | ||
| 39 | - fn clear(&mut self); | ||
| 40 | - fn draw(&mut self, c :&dyn Drawable<T>, ofs :Coordinate<T>, color :u32); | ||
| 41 | - fn put_text(&self, ofs :Coordinate<T>, s :&str); | ||
| 42 | - fn set_pixel(&mut self, c :Coordinate<T>, color :u32); | ||
| 43 | - fn show(&self); | ||
| 44 | -} | ||
| 45 | - | ||
| 46 | -pub trait Drawable<T> { | ||
| 47 | - fn plot(&self) -> Coordinates<T>; | ||
| 48 | -} | ||
| 49 | - | ||
| 50 | -pub trait Fillable<T> | ||
| 51 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 52 | - + Debug + Copy + From<i32> { | ||
| 53 | - fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32); | ||
| 54 | -} | ||
| 55 | - | ||
| 56 | -#[derive(Debug, Clone, Copy)] | ||
| 57 | -pub struct Coordinate<T>(pub i32, pub i32, pub T); | ||
| 58 | - | ||
| 59 | -#[derive(Debug, Clone)] | ||
| 60 | -pub struct Coordinates<T>(pub Vec<Coordinate<T>>); | ||
| 61 | - | ||
| 62 | -#[derive(Debug, Clone, Copy)] | ||
| 63 | -pub struct LineIterator<T> where T: Debug { | ||
| 64 | - a :Option<Coordinate<T>> | ||
| 65 | - , b :Coordinate<T> | ||
| 66 | - , dx :i32 | ||
| 67 | - , dy :i32 | ||
| 68 | - , dz :T | ||
| 69 | - , sx :i32 | ||
| 70 | - , sy :i32 | ||
| 71 | - , err :i32 | ||
| 72 | - , only_edges :bool | ||
| 73 | -} | ||
| 74 | - | ||
| 75 | -impl<T> Iterator for LineIterator<T> | ||
| 76 | -where T: Add<Output = T> + Debug + Copy + From<i32> { | ||
| 77 | - type Item = Coordinate<T>; | ||
| 78 | - | ||
| 79 | - fn next(&mut self) -> Option<Self::Item> { | ||
| 80 | - match self.a { | ||
| 81 | - None => None, | ||
| 82 | - Some(a) => { | ||
| 83 | - let Coordinate(ax, ay, az) = a; | ||
| 84 | - let Coordinate(bx, by, _) = self.b; | ||
| 85 | - | ||
| 86 | - if ax != bx || ay != by { | ||
| 87 | - match (2 * self.err >= self.dy, 2 * self.err <= self.dx ) { | ||
| 88 | - (true, false) => { | ||
| 89 | - let r = self.a; | ||
| 90 | - self.a = Some(Coordinate( ax + self.sx | ||
| 91 | - , ay | ||
| 92 | - , az + self.dz )); | ||
| 93 | - self.err = self.err + self.dy; | ||
| 94 | - if self.only_edges { self.next() } else { r } | ||
| 95 | - }, | ||
| 96 | - (false, true) => { | ||
| 97 | - let r = self.a; | ||
| 98 | - self.a = Some(Coordinate( ax | ||
| 99 | - , ay + self.sy | ||
| 100 | - , az + self.dz )); | ||
| 101 | - self.err = self.err + self.dx; | ||
| 102 | - r | ||
| 103 | - }, | ||
| 104 | - _ => { | ||
| 105 | - let r = self.a; | ||
| 106 | - self.a = Some(Coordinate( ax + self.sx | ||
| 107 | - , ay + self.sy | ||
| 108 | - , az + self.dz )); | ||
| 109 | - self.err = self.err + self.dx + self.dy; | ||
| 110 | - r | ||
| 111 | - }, | ||
| 112 | - } | ||
| 113 | - } else { | ||
| 114 | - self.a = None; | ||
| 115 | - Some(self.b) | ||
| 116 | - } | ||
| 117 | - } | ||
| 118 | - } | ||
| 119 | - } | ||
| 120 | -} | ||
| 121 | - | ||
| 122 | -impl<T> Coordinate<T> | ||
| 123 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 124 | - + Debug + Clone + Copy + From<i32> { | ||
| 125 | - fn iter(self, b :&Self, only_edges :bool) -> LineIterator<T> { | ||
| 126 | - let Coordinate(ax, ay, az) = self; | ||
| 127 | - let Coordinate(bx, by, bz) = *b; | ||
| 128 | - | ||
| 129 | - let dx = (bx - ax).abs(); | ||
| 130 | - let dy = -(by - ay).abs(); | ||
| 131 | - | ||
| 132 | - LineIterator { a: Some(self) | ||
| 133 | - , b: *b | ||
| 134 | - , dx: dx | ||
| 135 | - , dy: dy | ||
| 136 | - , dz: (bz - az) / cmp::max(dx, -dy).into() | ||
| 137 | - , sx: if ax < bx { 1 } else { -1 } | ||
| 138 | - , sy: if ay < by { 1 } else { -1 } | ||
| 139 | - , err: dx + dy | ||
| 140 | - , only_edges: only_edges | ||
| 141 | - } | ||
| 142 | - } | ||
| 143 | - | ||
| 144 | - fn line_iter(self, b :&Self) -> LineIterator<T> { | ||
| 145 | - self.iter(b, false) | ||
| 146 | - } | ||
| 147 | - | ||
| 148 | - fn line(self, b :&Self) -> Vec<Self> { | ||
| 149 | - self.line_iter(b).collect() | ||
| 150 | - } | ||
| 151 | - | ||
| 152 | - fn edge_iter(self, b :&Self) -> LineIterator<T> { | ||
| 153 | - self.iter(b, true) | ||
| 154 | - } | ||
| 155 | - | ||
| 156 | - fn edge(self, b :&Self) -> Vec<Self> { | ||
| 157 | - self.edge_iter(b).collect() | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - fn face(edges :&[Self]) -> Vec<Self> { | ||
| 161 | - edges.to_vec() | ||
| 162 | - } | ||
| 163 | -} | ||
| 164 | - | ||
| 165 | -impl<T> Display for Coordinate<T> { | ||
| 166 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 167 | - write!(f, "<{},{}>", self.0, self.1) | ||
| 168 | - } | ||
| 169 | -} | ||
| 170 | - | ||
| 171 | -impl<T> Display for Coordinates<T> where T: Copy { | ||
| 172 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 173 | - let Coordinates(is) = self; | ||
| 174 | - | ||
| 175 | - let c = match is[..] { | ||
| 176 | - [] => String::from(""), | ||
| 177 | - [a] => format!("{}", a), | ||
| 178 | - _ => { | ||
| 179 | - let mut a = format!("{}", is[0]); | ||
| 180 | - for i in is[1..].iter() { | ||
| 181 | - a = a + &format!(",{}", i); | ||
| 182 | - } | ||
| 183 | - a | ||
| 184 | - } | ||
| 185 | - }; | ||
| 186 | - | ||
| 187 | - write!(f, "Coordinates[{}]", c) | ||
| 188 | - } | ||
| 189 | -} | ||
| 190 | - | ||
| 191 | - | ||
| 192 | -#[derive(Debug, Clone, Copy)] | ||
| 193 | -pub struct Point<T>(pub Coordinate<T>); | ||
| 194 | - | ||
| 195 | -impl<T> Drawable<T> for Point<T> where T: Copy { | ||
| 196 | - fn plot(&self) -> Coordinates<T> { | ||
| 197 | - let Point(c) = *self; | ||
| 198 | - Coordinates(vec!(c)) | ||
| 199 | - } | ||
| 200 | -} | ||
| 201 | - | ||
| 202 | -impl<T> Display for Point<T> { | ||
| 203 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 204 | - let Point(p) = self; | ||
| 205 | - write!(f, "Point[{}]", p) | ||
| 206 | - } | ||
| 207 | -} | ||
| 208 | - | ||
| 209 | -#[derive(Debug, Clone, Copy)] | ||
| 210 | -pub struct Line<T>(pub Coordinate<T>, pub Coordinate<T>); | ||
| 211 | - | ||
| 212 | -impl<T> Drawable<T> for Line<T> | ||
| 213 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 214 | - + Debug + Clone + Copy + From<i32> { | ||
| 215 | - fn plot(&self) -> Coordinates<T> { | ||
| 216 | - let Line(a, b) = *self; | ||
| 217 | - Coordinates(a.line(&b)) | ||
| 218 | - } | ||
| 219 | -} | ||
| 220 | - | ||
| 221 | -impl<T> Display for Line<T> { | ||
| 222 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 223 | - let Line(a, b) = self; | ||
| 224 | - write!(f, "Line[{},{}]", a, b) | ||
| 225 | - } | ||
| 226 | -} | ||
| 227 | - | ||
| 228 | -#[derive(Debug, Clone)] | ||
| 229 | -pub struct Polyline<T>(pub Coordinates<T>); | ||
| 230 | - | ||
| 231 | -impl<T> Drawable<T> for Polyline<T> | ||
| 232 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 233 | - + Debug + Clone + Copy + From<i32> { | ||
| 234 | - fn plot(&self) -> Coordinates<T> { | ||
| 235 | - let Polyline(Coordinates(cs)) = self; | ||
| 236 | - | ||
| 237 | - match cs[..] { | ||
| 238 | - [] => Coordinates(Vec::<Coordinate<T>>::new()), | ||
| 239 | - [a] => Coordinates(vec!(a)), | ||
| 240 | - [a, b] => Coordinates(a.line(&b)), | ||
| 241 | - _ => { | ||
| 242 | - let (a, b) = (cs[0], cs[1]); | ||
| 243 | - let mut r = a.line(&b); | ||
| 244 | - let mut i = b; | ||
| 245 | - for j in cs[2..].iter() { | ||
| 246 | - r.append(&mut i.line(j)[1..].to_vec()); | ||
| 247 | - i = *j; | ||
| 248 | - } | ||
| 249 | - Coordinates(r) | ||
| 250 | - }, | ||
| 251 | - } | ||
| 252 | - } | ||
| 253 | -} | ||
| 254 | - | ||
| 255 | -impl<T> Display for Polyline<T> where T: Copy { | ||
| 256 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 257 | - let Polyline(a) = self; | ||
| 258 | - write!(f, "PLine[{}]", a) | ||
| 259 | - } | ||
| 260 | -} | ||
| 261 | - | ||
| 262 | -#[derive(Debug, Clone, Copy)] | ||
| 263 | -enum Direction { Left, Right } | ||
| 264 | - | ||
| 265 | -#[derive(Debug, Clone)] | ||
| 266 | -pub struct Polygon<T>(pub Coordinates<T>); | ||
| 267 | - | ||
| 268 | -#[derive(Debug, Clone)] | ||
| 269 | -enum VertexIteratorMode { Vertex, Edge } | ||
| 270 | -#[derive(Debug, Clone)] | ||
| 271 | -pub struct VertexIterator<'a,T> where T: Debug { | ||
| 272 | - p :&'a Polygon<T>, | ||
| 273 | - top :usize, | ||
| 274 | - current :Option<usize>, | ||
| 275 | - edge :Option<LineIterator<T>>, | ||
| 276 | - mode :VertexIteratorMode, | ||
| 277 | - direction :Direction, | ||
| 278 | -} | ||
| 279 | - | ||
| 280 | -impl<'a,T> VertexIterator<'a,T> | ||
| 281 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 282 | - + Debug + Copy + From<i32> { | ||
| 283 | - fn edge(p :&'a Polygon<T>, direction :Direction) -> Self { | ||
| 284 | - let top = p.vert_min(direction); | ||
| 285 | - let next = p.next_y(top, direction); | ||
| 286 | - let edge = match next { | ||
| 287 | - None => None, | ||
| 288 | - Some(next) => Some(p.vertex(top).edge_iter(&p.vertex(next))), | ||
| 289 | - }; | ||
| 290 | - | ||
| 291 | - VertexIterator { p: p | ||
| 292 | - , top: top | ||
| 293 | - , current: next | ||
| 294 | - , edge: edge | ||
| 295 | - , mode: VertexIteratorMode::Edge | ||
| 296 | - , direction: direction } | ||
| 297 | - } | ||
| 298 | - | ||
| 299 | - fn vertex(p :&'a Polygon<T>, direction :Direction) -> Self { | ||
| 300 | - let top = p.vert_min(direction); | ||
| 301 | - let next = p.next_y(top, direction); | ||
| 302 | - | ||
| 303 | - VertexIterator { p: p | ||
| 304 | - , top: top | ||
| 305 | - , current: next | ||
| 306 | - , edge: None | ||
| 307 | - , mode: VertexIteratorMode::Vertex | ||
| 308 | - , direction: direction } | ||
| 309 | - } | ||
| 310 | - | ||
| 311 | - // if this yields "None" we are finished. | ||
| 312 | - fn next_edge(&mut self) -> Option<LineIterator<T>> { | ||
| 313 | - let current = self.current?; | ||
| 314 | - let next = self.p.next_y(current, self.direction)?; | ||
| 315 | - let mut edge = self.p.vertex(current).edge_iter(&self.p.vertex(next)); | ||
| 316 | - | ||
| 317 | - match edge.next() { | ||
| 318 | - // It should be impossible that a new edge iterator has no values | ||
| 319 | - // at all… anyway, just in case I handle it here. | ||
| 320 | - None => self.next_edge(), | ||
| 321 | - Some(_) => { | ||
| 322 | - self.current = Some(next); | ||
| 323 | - self.edge = Some(edge); | ||
| 324 | - self.edge | ||
| 325 | - }, | ||
| 326 | - } | ||
| 327 | - } | ||
| 328 | -} | ||
| 329 | - | ||
| 330 | -impl<'a,T> Iterator for VertexIterator<'a,T> | ||
| 331 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 332 | - + Debug + Copy + From<i32> { | ||
| 333 | - type Item = Coordinate<T>; | ||
| 334 | - | ||
| 335 | - fn next(&mut self) -> Option<Self::Item> { | ||
| 336 | - match self.mode { | ||
| 337 | - VertexIteratorMode::Edge => { | ||
| 338 | - // if for whatever reason edge is "None" finish this iterator. | ||
| 339 | - let next = self.edge.as_mut()?.next(); | ||
| 340 | - | ||
| 341 | - match next { | ||
| 342 | - Some(_) => next, | ||
| 343 | - None => { | ||
| 344 | - self.next_edge()?; | ||
| 345 | - self.next() | ||
| 346 | - }, | ||
| 347 | - } | ||
| 348 | - }, | ||
| 349 | - VertexIteratorMode::Vertex => { | ||
| 350 | - let current = self.current?; | ||
| 351 | - self.current = self.p.next_y(current, self.direction); | ||
| 352 | - Some(self.p.vertex(current)) | ||
| 353 | - }, | ||
| 354 | - } | ||
| 355 | - } | ||
| 356 | -} | ||
| 357 | - | ||
| 358 | -impl<T> Polygon<T> | ||
| 359 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 360 | - + Copy + Debug + From<i32> { | ||
| 361 | - #[inline] | ||
| 362 | - fn vertex(&self, v :usize) -> Coordinate<T> { | ||
| 363 | - let Polygon(Coordinates(cs)) = self; | ||
| 364 | - cs[v] | ||
| 365 | - } | ||
| 366 | - | ||
| 367 | - fn vert_min<'a>(&'a self, d :Direction) -> usize { | ||
| 368 | - let Polygon(Coordinates(cs)) = self; | ||
| 369 | - | ||
| 370 | - type ICoord<'a,T> = (usize, &'a Coordinate<T>); | ||
| 371 | - | ||
| 372 | - // TODO I guess the problem here is that it does not account for the | ||
| 373 | - // same y vertex on the beggining and the end. So i guess correct | ||
| 374 | - // would be finding the first one and then dependings on the | ||
| 375 | - // given direction either search left or right for same y's. | ||
| 376 | - let fold = |acc :Option<ICoord<'a,T>>, x :ICoord<'a,T>| | ||
| 377 | - match acc { | ||
| 378 | - None => Some(x), | ||
| 379 | - Some(a) => { | ||
| 380 | - let Coordinate(_, ay, _) = a.1; | ||
| 381 | - let Coordinate(_, xy, _) = x.1; | ||
| 382 | - if xy < ay {Some(x)} else {Some(a)} | ||
| 383 | - }, | ||
| 384 | - }; | ||
| 385 | - | ||
| 386 | - let mut min = cs.iter().enumerate().fold(None, fold).unwrap().0; | ||
| 387 | - let mut next = self.step(min, d); | ||
| 388 | - | ||
| 389 | - while self.vertex(min).1 == self.vertex(next).1 { | ||
| 390 | - min = next; | ||
| 391 | - next = self.step(min, d); | ||
| 392 | - } | ||
| 393 | - | ||
| 394 | - min | ||
| 395 | - } | ||
| 396 | - | ||
| 397 | - fn left_edge(&self) -> VertexIterator<T> { | ||
| 398 | - VertexIterator::edge(self, Direction::Left) | ||
| 399 | - } | ||
| 400 | - | ||
| 401 | - fn right_edge(&self) -> VertexIterator<T> { | ||
| 402 | - VertexIterator::edge(self, Direction::Right) | ||
| 403 | - } | ||
| 404 | - | ||
| 405 | - fn left_vertices(&self) -> VertexIterator<T> { | ||
| 406 | - VertexIterator::vertex(self, Direction::Left) | ||
| 407 | - } | ||
| 408 | - | ||
| 409 | - fn right_vertices(&self) -> VertexIterator<T> { | ||
| 410 | - VertexIterator::vertex(self, Direction::Right) | ||
| 411 | - } | ||
| 412 | - | ||
| 413 | - fn left(&self, v :usize) -> usize { | ||
| 414 | - let Polygon(Coordinates(cs)) = self; | ||
| 415 | - | ||
| 416 | - match v { | ||
| 417 | - 0 => cs.len() - 1, | ||
| 418 | - _ => v - 1, | ||
| 419 | - } | ||
| 420 | - } | ||
| 421 | - | ||
| 422 | - fn right(&self, v :usize) -> usize { | ||
| 423 | - let Polygon(Coordinates(cs)) = self; | ||
| 424 | - | ||
| 425 | - (v + 1) % cs.len() | ||
| 426 | - } | ||
| 427 | - | ||
| 428 | - fn step(&self, v :usize, d :Direction) -> usize { | ||
| 429 | - match d { | ||
| 430 | - Direction::Left => self.left(v), | ||
| 431 | - Direction::Right => self.right(v), | ||
| 432 | - } | ||
| 433 | - } | ||
| 434 | - | ||
| 435 | - fn next_y(&self, c :usize, d :Direction) -> Option<usize> { | ||
| 436 | - fn inner<T>( p :&Polygon<T> | ||
| 437 | - , c :usize | ||
| 438 | - , n :usize | ||
| 439 | - , d :Direction) -> Option<usize> | ||
| 440 | - where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 441 | - + Copy + Debug + From<i32> { | ||
| 442 | - if c == n { | ||
| 443 | - None | ||
| 444 | - } else { | ||
| 445 | - let Coordinate(_, cy, _) = p.vertex(c); | ||
| 446 | - let Coordinate(_, ny, _) = p.vertex(n); | ||
| 447 | - | ||
| 448 | - if ny < cy { None } else { Some(n) } | ||
| 449 | - } | ||
| 450 | - } | ||
| 451 | - | ||
| 452 | - inner(self, c, self.step(c, d), d) | ||
| 453 | - } | ||
| 454 | - | ||
| 455 | - pub fn debug(&self) { | ||
| 456 | - let mut left = self.left_vertices(); | ||
| 457 | - let mut right = self.right_vertices(); | ||
| 458 | - | ||
| 459 | - if left.find(|l| right.find(|r| l.0 == r.0).is_some()).is_some() { | ||
| 460 | - let left :Vec<Coordinate<T>> = self.left_vertices().collect(); | ||
| 461 | - let right :Vec<Coordinate<T>> = self.right_vertices().collect(); | ||
| 462 | - | ||
| 463 | - println!("==="); | ||
| 464 | - println!("== poly : {:?}", self); | ||
| 465 | - println!("== ltop : {:?}", self.vert_min(Direction::Left)); | ||
| 466 | - println!("== rtop : {:?}", self.vert_min(Direction::Right)); | ||
| 467 | - println!("== left : {:?}", left); | ||
| 468 | - println!("== right : {:?}", right); | ||
| 469 | - println!("==="); | ||
| 470 | - } | ||
| 471 | - } | ||
| 472 | -} | ||
| 473 | - | ||
| 474 | -impl<T> Drawable<T> for Polygon<T> | ||
| 475 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 476 | - + Debug + Clone + Copy + From<i32> { | ||
| 477 | - fn plot(&self) -> Coordinates<T> { | ||
| 478 | - let Polygon(Coordinates(cs)) = self; | ||
| 479 | - | ||
| 480 | - match cs[..] { | ||
| 481 | - [] => Coordinates(Vec::<Coordinate<T>>::new()), | ||
| 482 | - [a] => Coordinates(vec!(a)), | ||
| 483 | - [a, b] => Coordinates(a.line(&b)), | ||
| 484 | - _ => { | ||
| 485 | - let (a, b) = (cs[0], cs[1]); | ||
| 486 | - let mut r = a.line(&b); | ||
| 487 | - let mut i = b; | ||
| 488 | - for j in cs[2..].iter() { | ||
| 489 | - r.append(&mut i.line(j)[1..].to_vec()); | ||
| 490 | - i = *j; | ||
| 491 | - } | ||
| 492 | - let mut j = a.line(&i); | ||
| 493 | - let l = j.len(); | ||
| 494 | - if l > 1 { | ||
| 495 | - r.append(&mut j[1..l-1].to_vec()); | ||
| 496 | - } | ||
| 497 | - Coordinates(r) | ||
| 498 | - }, | ||
| 499 | - } | ||
| 500 | - } | ||
| 501 | -} | ||
| 502 | - | ||
| 503 | -impl<T> Fillable<T> for Polygon<T> | ||
| 504 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 505 | - + Debug + Clone + Copy + From<i32> { | ||
| 506 | - fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32) { | ||
| 507 | - let scanlines = self.left_edge().zip(self.right_edge()); | ||
| 508 | - | ||
| 509 | - for l in scanlines.flat_map(|(l, r)| l.line_iter(&r)) { | ||
| 510 | - canvas.set_pixel(l, color); | ||
| 511 | - } | ||
| 512 | - } | ||
| 513 | -} | ||
| 514 | - | ||
| 515 | -impl<T> Display for Polygon<T> where T: Copy { | ||
| 516 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 517 | - let Polygon(a) = self; | ||
| 518 | - write!(f, "Poly[{}]", a) | ||
| 519 | - } | ||
| 520 | -} |
src/easel/canvas.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use std::fmt::{Debug, Display, Formatter, Result}; | ||
| 23 | +use std::ops::{Add, Div, Sub}; | ||
| 24 | +use std::sync::mpsc; | ||
| 25 | + | ||
| 26 | +use super::drawable::Drawable; | ||
| 27 | +use super::line_iterator::LineIterator; | ||
| 28 | + | ||
| 29 | +// A 2D drawing surface. | ||
| 30 | +pub trait Canvas<T> { | ||
| 31 | + fn init_events(&self); | ||
| 32 | + fn start_events(&self, tx :mpsc::Sender<i32>); | ||
| 33 | + | ||
| 34 | + fn width(&self) -> u16; | ||
| 35 | + fn height(&self) -> u16; | ||
| 36 | + | ||
| 37 | + fn clear(&mut self); | ||
| 38 | + fn draw(&mut self, c :&dyn Drawable<T>, color :u32); | ||
| 39 | + fn set_pixel(&mut self, c :Vertex<T>, color :u32); | ||
| 40 | + fn put_text(&self, ofs :Vertex<T>, s :&str); | ||
| 41 | + fn show(&self); | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +// A Vertex is a position on a 2D drawing surface along other stuff | ||
| 45 | +// that needs to iterate between those coordinates. | ||
| 46 | +#[derive(Debug, Clone, Copy)] | ||
| 47 | +pub struct Vertex<T>{ x :i32 // canvas x coordinate | ||
| 48 | + , y :i32 // canvas y coordinate | ||
| 49 | + , zr :T } // z reciprocal from 3D projection. | ||
| 50 | + | ||
| 51 | +pub type Vertices<T> = Vec<Vertex<T>>; | ||
| 52 | + | ||
| 53 | +impl<T> Vertex<T> | ||
| 54 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 55 | + + Debug + Clone + Copy + From<i32> { | ||
| 56 | + pub fn new(x :i32, y :i32, zr :T) -> Self { | ||
| 57 | + Vertex{x, y, zr} | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + #[inline] | ||
| 61 | + pub fn as_tuple(&self) -> (i32, i32, T) { | ||
| 62 | + (self.x, self.y, self.zr) | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + #[inline] | ||
| 66 | + pub fn same_x(&self, b :&Self) -> bool { | ||
| 67 | + self.x == b.x | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + #[inline] | ||
| 71 | + pub fn same_y(&self, b :&Self) -> bool { | ||
| 72 | + self.y == b.y | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + #[inline] | ||
| 76 | + pub fn same_position(&self, b :&Self) -> bool { | ||
| 77 | + self.same_x(b) && self.same_y(b) | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + fn iter(self, b :Self, only_edges :bool) -> LineIterator<T> { | ||
| 81 | + LineIterator::new(self, b, only_edges) | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + pub fn line_iter(self, b :Self) -> LineIterator<T> { | ||
| 85 | + self.iter(b, false) | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + pub fn line(self, b :Self) -> Vec<Self> { | ||
| 89 | + self.line_iter(b).collect() | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + pub fn edge_iter(self, b :Self) -> LineIterator<T> { | ||
| 93 | + self.iter(b, true) | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + fn edge(self, b :Self) -> Vec<Self> { | ||
| 97 | + self.edge_iter(b).collect() | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + fn face(edges :&[Self]) -> Vec<Self> { | ||
| 101 | + edges.to_vec() | ||
| 102 | + } | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +impl<T> Display for Vertex<T> { | ||
| 106 | + fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 107 | + write!(f, "<{},{}>", self.x, self.y) | ||
| 108 | + } | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +impl<T> Add for Vertex<T> where T: Add<Output=T> { | ||
| 112 | + type Output = Self; | ||
| 113 | + | ||
| 114 | + fn add(self, other :Self) -> Vertex<T> { | ||
| 115 | + Vertex{ x: self.x + other.x | ||
| 116 | + , y: self.y + other.y | ||
| 117 | + , zr: self.zr + other.zr } | ||
| 118 | + } | ||
| 119 | +} |
src/easel/drawable.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use super::canvas::Vertices; | ||
| 23 | + | ||
| 24 | +pub trait Drawable<T> { | ||
| 25 | + fn plot(&self) -> Vertices<T>; | ||
| 26 | +} |
src/easel/fillable.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use std::fmt::Debug; | ||
| 23 | +use std::ops::{Add, Div, Sub}; | ||
| 24 | + | ||
| 25 | +use super::canvas::Canvas; | ||
| 26 | + | ||
| 27 | +pub trait Fillable<T> | ||
| 28 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 29 | + + Debug + Copy + From<i32> { | ||
| 30 | + fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32); | ||
| 31 | +} |
src/easel/line.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use std::fmt::{Debug, Display, Formatter, Result}; | ||
| 23 | +use std::ops::{Add, Div, Sub}; | ||
| 24 | + | ||
| 25 | +use super::canvas::{Vertex, Vertices}; | ||
| 26 | +use super::drawable::Drawable; | ||
| 27 | + | ||
| 28 | +#[derive(Debug, Clone, Copy)] | ||
| 29 | +pub struct Line<T>(pub Vertex<T>, pub Vertex<T>); | ||
| 30 | + | ||
| 31 | +impl<T> Drawable<T> for Line<T> | ||
| 32 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 33 | + + Debug + Clone + Copy + From<i32> { | ||
| 34 | + fn plot(&self) -> Vertices<T> { | ||
| 35 | + let Line(a, b) = *self; | ||
| 36 | + a.line(b) | ||
| 37 | + } | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +impl<T> Display for Line<T> { | ||
| 41 | + fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 42 | + let Line(a, b) = self; | ||
| 43 | + write!(f, "Line[{},{}]", a, b) | ||
| 44 | + } | ||
| 45 | +} |
src/easel/line_iterator.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use std::cmp::max; | ||
| 23 | +use std::fmt::Debug; | ||
| 24 | +use std::ops::{Add, Div, Sub}; | ||
| 25 | + | ||
| 26 | +use super::canvas::Vertex; | ||
| 27 | + | ||
| 28 | +#[derive(Debug, Clone, Copy)] | ||
| 29 | +pub struct LineIterator<T> where T: Debug { | ||
| 30 | + a :Option<Vertex<T>> | ||
| 31 | + , b :Vertex<T> | ||
| 32 | + , dx :i32 | ||
| 33 | + , dy :i32 | ||
| 34 | + , incx :Vertex<T> | ||
| 35 | + , incy :Vertex<T> | ||
| 36 | + , incxy :Vertex<T> | ||
| 37 | + , err :i32 | ||
| 38 | + , edges :bool | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +impl<T> LineIterator<T> | ||
| 42 | +where T: Add<Output=T> + Div<Output=T> + Sub<Output=T> | ||
| 43 | + + Debug + Clone + Copy + From<i32> { | ||
| 44 | + pub fn new(a :Vertex<T>, b :Vertex<T>, edges :bool) -> LineIterator<T> { | ||
| 45 | + let (ax, ay, azr) = a.as_tuple(); | ||
| 46 | + let (bx, by, bzr) = b.as_tuple(); | ||
| 47 | + | ||
| 48 | + let dx = (bx - ax).abs(); | ||
| 49 | + let dy = -(by - ay).abs(); | ||
| 50 | + let dz = (bzr - azr) / max(dx, -dy).into(); | ||
| 51 | + | ||
| 52 | + let sx = if ax < bx { 1 } else { -1 }; | ||
| 53 | + let sy = if ay < by { 1 } else { -1 }; | ||
| 54 | + | ||
| 55 | + LineIterator { a: Some(a) | ||
| 56 | + , b: b | ||
| 57 | + , dx: dx | ||
| 58 | + , dy: dy | ||
| 59 | + , incx: Vertex::new( sx, 0.into(), dz) | ||
| 60 | + , incy: Vertex::new(0.into(), sy, dz) | ||
| 61 | + , incxy: Vertex::new( sx, sy, dz) | ||
| 62 | + , err: dx + dy | ||
| 63 | + , edges: edges } | ||
| 64 | + } | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +impl<T> Iterator for LineIterator<T> | ||
| 68 | +where T: Add<Output=T> + Div<Output=T> + Sub<Output=T> | ||
| 69 | + + Debug + Copy + From<i32> { | ||
| 70 | + type Item = Vertex<T>; | ||
| 71 | + | ||
| 72 | + // Bresenham based line iteration. | ||
| 73 | + fn next(&mut self) -> Option<Self::Item> { | ||
| 74 | + if ! self.a?.same_position(&self.b) { | ||
| 75 | + let ret = self.a; | ||
| 76 | + let inc = match (2*self.err >= self.dy, 2*self.err <= self.dx ) { | ||
| 77 | + (true, false) => ( self.incx, self.dy, self.edges), | ||
| 78 | + (false, true) => ( self.incy, self.dx, false), | ||
| 79 | + _ => (self.incxy, self.dx+self.dy, false), | ||
| 80 | + }; | ||
| 81 | + | ||
| 82 | + self.a = Some(self.a? + inc.0); | ||
| 83 | + self.err = self.err + inc.1; | ||
| 84 | + if inc.2 { self.next() } else { ret } | ||
| 85 | + } else { | ||
| 86 | + self.a = None; | ||
| 87 | + Some(self.b) | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | +} |
src/easel/mod.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +pub trait Easel { | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +// Trait to implement for a concrete canvas as well as struct for canvas | ||
| 26 | +// coordinates (which also includes z reciprocal) | ||
| 27 | +pub mod canvas; | ||
| 28 | + | ||
| 29 | +// Traits that new drawing primitives must implement to be drawn on a canvas. | ||
| 30 | +pub mod drawable; | ||
| 31 | +pub mod fillable; | ||
| 32 | + | ||
| 33 | +// Drawing primitives | ||
| 34 | +pub mod line; | ||
| 35 | +pub mod point; | ||
| 36 | +pub mod polygon; | ||
| 37 | +pub mod polyline; | ||
| 38 | + | ||
| 39 | +// Helper iterators to find all positions a primitive needs to draw. | ||
| 40 | +mod line_iterator; | ||
| 41 | +mod vertex_iterator; |
src/easel/point.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use std::fmt::{Display, Formatter, Result}; | ||
| 23 | + | ||
| 24 | +use super::canvas::{Vertex, Vertices}; | ||
| 25 | +use super::drawable::Drawable; | ||
| 26 | + | ||
| 27 | +#[derive(Debug, Clone, Copy)] | ||
| 28 | +pub struct Point<T>(pub Vertex<T>); | ||
| 29 | + | ||
| 30 | +impl<T> Drawable<T> for Point<T> where T: Copy { | ||
| 31 | + fn plot(&self) -> Vertices<T> { | ||
| 32 | + let Point(c) = *self; | ||
| 33 | + vec!(c) | ||
| 34 | + } | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +impl<T> Display for Point<T> { | ||
| 38 | + fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 39 | + let Point(p) = self; | ||
| 40 | + write!(f, "Point[{}]", p) | ||
| 41 | + } | ||
| 42 | +} |
src/easel/polygon.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use std::fmt::Debug; | ||
| 23 | +use std::ops::{Add, Div, Sub}; | ||
| 24 | + | ||
| 25 | +use super::canvas::{Canvas, Vertex, Vertices}; | ||
| 26 | +use super::drawable::Drawable; | ||
| 27 | +use super::fillable::Fillable; | ||
| 28 | +use super::vertex_iterator::{Direction, VertexIterator}; | ||
| 29 | + | ||
| 30 | +#[derive(Debug, Clone)] | ||
| 31 | +pub struct Polygon<T>(pub Vertices<T>); | ||
| 32 | + | ||
| 33 | +impl<T> Polygon<T> | ||
| 34 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 35 | + + Copy + Debug + From<i32> { | ||
| 36 | + #[inline] | ||
| 37 | + pub fn vertex(&self, v :usize) -> Vertex<T> { | ||
| 38 | + let Polygon(cs) = self; | ||
| 39 | + cs[v] | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + pub fn vert_min<'a>(&'a self, d :Direction) -> usize { | ||
| 43 | + let Polygon(cs) = self; | ||
| 44 | + | ||
| 45 | + type ICoord<'a, T> = (usize, &'a Vertex<T>); | ||
| 46 | + | ||
| 47 | + let fold = |acc :Option<ICoord<'a,T>>, x :ICoord<'a, T>| | ||
| 48 | + match acc { | ||
| 49 | + None => Some(x), | ||
| 50 | + Some(a) => { | ||
| 51 | + let (_, ay, _) = a.1.as_tuple(); | ||
| 52 | + let (_, xy, _) = x.1.as_tuple(); | ||
| 53 | + if xy < ay {Some(x)} else {Some(a)} | ||
| 54 | + }, | ||
| 55 | + }; | ||
| 56 | + | ||
| 57 | + let mut min = cs.iter().enumerate().fold(None, fold).unwrap().0; | ||
| 58 | + let mut next = self.step(min, d); | ||
| 59 | + | ||
| 60 | + while self.vertex(min).same_y(&self.vertex(next)) { | ||
| 61 | + min = next; | ||
| 62 | + next = self.step(min, d); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + min | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + fn left_edge(&self) -> VertexIterator<T> { | ||
| 69 | + VertexIterator::edge(self, Direction::Left) | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + fn right_edge(&self) -> VertexIterator<T> { | ||
| 73 | + VertexIterator::edge(self, Direction::Right) | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + fn left_vertices(&self) -> VertexIterator<T> { | ||
| 77 | + VertexIterator::line(self, Direction::Left) | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + fn right_vertices(&self) -> VertexIterator<T> { | ||
| 81 | + VertexIterator::line(self, Direction::Right) | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + fn left(&self, v :usize) -> usize { | ||
| 85 | + let Polygon(cs) = self; | ||
| 86 | + | ||
| 87 | + match v { | ||
| 88 | + 0 => cs.len() - 1, | ||
| 89 | + _ => v - 1, | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + fn right(&self, v :usize) -> usize { | ||
| 94 | + let Polygon(cs) = self; | ||
| 95 | + | ||
| 96 | + (v + 1) % cs.len() | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + fn step(&self, v :usize, d :Direction) -> usize { | ||
| 100 | + match d { | ||
| 101 | + Direction::Left => self.left(v), | ||
| 102 | + Direction::Right => self.right(v), | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + pub fn next_y(&self, c :usize, d :Direction) -> Option<usize> { | ||
| 107 | + fn inner<T>( p :&Polygon<T> | ||
| 108 | + , c :usize | ||
| 109 | + , n :usize | ||
| 110 | + , d :Direction) -> Option<usize> | ||
| 111 | + where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 112 | + + Copy + Debug + From<i32> { | ||
| 113 | + if c == n { | ||
| 114 | + None | ||
| 115 | + } else { | ||
| 116 | + let (_, cy, _) = p.vertex(c).as_tuple(); | ||
| 117 | + let (_, ny, _) = p.vertex(n).as_tuple(); | ||
| 118 | + | ||
| 119 | + if ny < cy { None } else { Some(n) } | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + inner(self, c, self.step(c, d), d) | ||
| 124 | + } | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +impl<T> Drawable<T> for Polygon<T> | ||
| 128 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 129 | + + Debug + Clone + Copy + From<i32> { | ||
| 130 | + fn plot(&self) -> Vertices<T> { | ||
| 131 | + let Polygon(cs) = self; | ||
| 132 | + | ||
| 133 | + match cs[..] { | ||
| 134 | + [] => Vec::<Vertex<T>>::new(), | ||
| 135 | + [a] => vec!(a), | ||
| 136 | + [a, b] => a.line(b), | ||
| 137 | + _ => { | ||
| 138 | + let (a, b) = (cs[0], cs[1]); | ||
| 139 | + let mut r = a.line(b); | ||
| 140 | + let mut i = b; | ||
| 141 | + for j in cs[2..].iter() { | ||
| 142 | + r.append(&mut i.line(*j)[1..].to_vec()); | ||
| 143 | + i = *j; | ||
| 144 | + } | ||
| 145 | + let mut j = a.line(i); | ||
| 146 | + let l = j.len(); | ||
| 147 | + if l > 1 { | ||
| 148 | + r.append(&mut j[1..l-1].to_vec()); | ||
| 149 | + } | ||
| 150 | + r | ||
| 151 | + }, | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +impl<T> Fillable<T> for Polygon<T> | ||
| 157 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 158 | + + Debug + Clone + Copy + From<i32> { | ||
| 159 | + fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32) { | ||
| 160 | + let scanlines = self.left_edge().zip(self.right_edge()); | ||
| 161 | + let vertices = |(l, r) :(Vertex<T>, Vertex<T>)| l.line_iter(r); | ||
| 162 | + | ||
| 163 | + for p in scanlines.flat_map(vertices) { | ||
| 164 | + canvas.set_pixel(p, color); | ||
| 165 | + } | ||
| 166 | + } | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +/* | ||
| 170 | +impl<T> Display for Polygon<T> where T: Copy { | ||
| 171 | + fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 172 | + let Polygon(a) = self; | ||
| 173 | + write!(f, "Poly[{}]", a) | ||
| 174 | + } | ||
| 175 | +} | ||
| 176 | +*/ |
src/easel/polyline.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | + | ||
| 22 | +use std::fmt::Debug; | ||
| 23 | +use std::ops::{Add, Div, Sub}; | ||
| 24 | + | ||
| 25 | +use super::canvas::{Vertex, Vertices}; | ||
| 26 | +use super::drawable::Drawable; | ||
| 27 | + | ||
| 28 | +#[derive(Debug, Clone)] | ||
| 29 | +pub struct Polyline<T>(pub Vertices<T>); | ||
| 30 | + | ||
| 31 | +impl<T> Drawable<T> for Polyline<T> | ||
| 32 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 33 | + + Debug + Clone + Copy + From<i32> { | ||
| 34 | + fn plot(&self) -> Vertices<T> { | ||
| 35 | + let Polyline(cs) = self; | ||
| 36 | + | ||
| 37 | + match cs[..] { | ||
| 38 | + [] => Vec::<Vertex<T>>::new(), | ||
| 39 | + [a] => vec!(a), | ||
| 40 | + [a, b] => a.line(b), | ||
| 41 | + _ => { | ||
| 42 | + let (a, b) = (cs[0], cs[1]); | ||
| 43 | + let mut r = a.line(b); | ||
| 44 | + let mut i = b; | ||
| 45 | + for j in cs[2..].iter() { | ||
| 46 | + r.append(&mut i.line(*j)[1..].to_vec()); | ||
| 47 | + i = *j; | ||
| 48 | + } | ||
| 49 | + r | ||
| 50 | + }, | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | +} | ||
| 54 | +/* | ||
| 55 | +impl<T> Display for Polyline<T> where T: Copy { | ||
| 56 | + fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 57 | + let Polyline(a) = self; | ||
| 58 | + write!(f, "PLine[{}]", a) | ||
| 59 | + } | ||
| 60 | +} | ||
| 61 | +*/ |
src/easel/vertex_iterator.rs
0 → 100644
| 1 | +// | ||
| 2 | +// … | ||
| 3 | +// | ||
| 4 | +// Georg Hopp <georg@steffers.org> | ||
| 5 | +// | ||
| 6 | +// Copyright © 2019 Georg Hopp | ||
| 7 | +// | ||
| 8 | +// This program is free software: you can redistribute it and/or modify | ||
| 9 | +// it under the terms of the GNU General Public License as published by | ||
| 10 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +// (at your option) any later version. | ||
| 12 | +// | ||
| 13 | +// This program is distributed in the hope that it will be useful, | ||
| 14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +// GNU General Public License for more details. | ||
| 17 | +// | ||
| 18 | +// You should have received a copy of the GNU General Public License | ||
| 19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +// | ||
| 21 | +use std::fmt::Debug; | ||
| 22 | +use std::ops::{Add, Sub, Div}; | ||
| 23 | + | ||
| 24 | +use super::line_iterator::LineIterator; | ||
| 25 | +use super::canvas::Vertex; | ||
| 26 | +use super::polygon::Polygon; | ||
| 27 | + | ||
| 28 | +#[derive(Debug, Clone, Copy)] | ||
| 29 | +pub enum Direction { | ||
| 30 | + Left, | ||
| 31 | + Right, | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +#[derive(Debug, Clone)] | ||
| 35 | +enum VertexIteratorMode { | ||
| 36 | + Line, | ||
| 37 | + Edge | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +#[derive(Debug, Clone)] | ||
| 41 | +pub struct VertexIterator<'a, T> where T: Debug { | ||
| 42 | + p :&'a Polygon<T>, | ||
| 43 | + top :usize, | ||
| 44 | + current :Option<usize>, | ||
| 45 | + edge :Option<LineIterator<T>>, | ||
| 46 | + mode :VertexIteratorMode, | ||
| 47 | + direction :Direction, | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +impl<'a, T> VertexIterator<'a, T> | ||
| 51 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 52 | + + Debug + Copy + From<i32> { | ||
| 53 | + pub fn line(p :&'a Polygon<T>, direction :Direction) -> Self { | ||
| 54 | + let top = p.vert_min(direction); | ||
| 55 | + let next = p.next_y(top, direction); | ||
| 56 | + | ||
| 57 | + VertexIterator { p: p | ||
| 58 | + , top: top | ||
| 59 | + , current: next | ||
| 60 | + , edge: None | ||
| 61 | + , mode: VertexIteratorMode::Line | ||
| 62 | + , direction: direction } | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + pub fn edge(p :&'a Polygon<T>, direction :Direction) -> Self { | ||
| 66 | + let mut vi = Self::line(p, direction); | ||
| 67 | + | ||
| 68 | + vi.mode = VertexIteratorMode::Edge; | ||
| 69 | + vi.edge = match vi.current { | ||
| 70 | + None => None, | ||
| 71 | + Some(next) => Some(p.vertex(vi.top).edge_iter(p.vertex(next))), | ||
| 72 | + }; | ||
| 73 | + | ||
| 74 | + vi | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + // if this yields "None" we are finished. | ||
| 78 | + fn next_edge(&mut self) -> Option<LineIterator<T>> { | ||
| 79 | + let current = self.current?; | ||
| 80 | + let next = self.p.next_y(current, self.direction)?; | ||
| 81 | + let mut edge = self.p.vertex(current).edge_iter(self.p.vertex(next)); | ||
| 82 | + | ||
| 83 | + match edge.next() { | ||
| 84 | + // It should be impossible that a new edge iterator has no values | ||
| 85 | + // at all… anyway, just in case I handle it here. | ||
| 86 | + None => self.next_edge(), | ||
| 87 | + Some(_) => { | ||
| 88 | + self.current = Some(next); | ||
| 89 | + self.edge = Some(edge); | ||
| 90 | + self.edge | ||
| 91 | + }, | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +impl<'a,T> Iterator for VertexIterator<'a,T> | ||
| 97 | +where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 98 | + + Debug + Copy + From<i32> { | ||
| 99 | + type Item = Vertex<T>; | ||
| 100 | + | ||
| 101 | + fn next(&mut self) -> Option<Self::Item> { | ||
| 102 | + match self.mode { | ||
| 103 | + VertexIteratorMode::Edge => { | ||
| 104 | + // if for whatever reason edge is "None" finish this iterator. | ||
| 105 | + let next = self.edge.as_mut()?.next(); | ||
| 106 | + | ||
| 107 | + match next { | ||
| 108 | + Some(_) => next, | ||
| 109 | + None => { | ||
| 110 | + self.next_edge()?; | ||
| 111 | + self.next() | ||
| 112 | + }, | ||
| 113 | + } | ||
| 114 | + }, | ||
| 115 | + VertexIteratorMode::Line => { | ||
| 116 | + let current = self.current?; | ||
| 117 | + self.current = self.p.next_y(current, self.direction); | ||
| 118 | + Some(self.p.vertex(current)) | ||
| 119 | + }, | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | +} |
| @@ -22,7 +22,8 @@ use std::convert::{From, Into}; | @@ -22,7 +22,8 @@ use std::convert::{From, Into}; | ||
| 22 | use std::ops::{Add,Sub,Neg,Mul,Div}; | 22 | use std::ops::{Add,Sub,Neg,Mul,Div}; |
| 23 | use std::fmt::Debug; | 23 | use std::fmt::Debug; |
| 24 | 24 | ||
| 25 | -use crate::easel::{Canvas, Coordinate, Coordinates, Polygon}; | 25 | +use crate::easel::canvas::{Canvas, Vertex}; |
| 26 | +use crate::easel::polygon::Polygon; | ||
| 26 | use crate::transform::{TMatrix, Transformable}; | 27 | use crate::transform::{TMatrix, Transformable}; |
| 27 | use crate::trigonometry::Trig; | 28 | use crate::trigonometry::Trig; |
| 28 | use crate::vector::Vector; | 29 | use crate::vector::Vector; |
| @@ -251,20 +252,15 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | @@ -251,20 +252,15 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 251 | let f2 :T = 2.into(); | 252 | let f2 :T = 2.into(); |
| 252 | let ch = a / (f2 * T::sqrt(f2).unwrap()); | 253 | let ch = a / (f2 * T::sqrt(f2).unwrap()); |
| 253 | 254 | ||
| 254 | - let ps = vec!( Point::new(-ch, -ch, ch) // A | ||
| 255 | - , Point::new(-ch, ch, -ch) // C | ||
| 256 | - , Point::new( ch, -ch, -ch) // E | ||
| 257 | - , Point::new( ch, ch, ch) ); // G | 255 | + let ps = vec!( Point::new(-ch, -ch, ch) |
| 256 | + , Point::new(-ch, ch, -ch) | ||
| 257 | + , Point::new( ch, -ch, -ch) | ||
| 258 | + , Point::new( ch, ch, ch) ); | ||
| 258 | 259 | ||
| 259 | - // bottom: 1, 2, 3 | ||
| 260 | let fs = vec!( Face::new(vec!(2, 1, 0), &ps) // bottom | 260 | let fs = vec!( Face::new(vec!(2, 1, 0), &ps) // bottom |
| 261 | , Face::new(vec!(3, 2, 0), &ps) | 261 | , Face::new(vec!(3, 2, 0), &ps) |
| 262 | , Face::new(vec!(0, 1, 3), &ps) | 262 | , Face::new(vec!(0, 1, 3), &ps) |
| 263 | , Face::new(vec!(1, 2, 3), &ps) ); | 263 | , Face::new(vec!(1, 2, 3), &ps) ); |
| 264 | - //let fs = vec!( Face::new(vec!(0, 1, 2), &ps) // bottom | ||
| 265 | - // , Face::new(vec!(0, 2, 3), &ps) | ||
| 266 | - // , Face::new(vec!(3, 1, 0), &ps) | ||
| 267 | - // , Face::new(vec!(3, 2, 1), &ps) ); | ||
| 268 | 264 | ||
| 269 | Polyeder{ points: ps, faces: fs } | 265 | Polyeder{ points: ps, faces: fs } |
| 270 | } | 266 | } |
| @@ -335,15 +331,15 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | @@ -335,15 +331,15 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 335 | // Helper to create a Polygon from Coordinates… | 331 | // Helper to create a Polygon from Coordinates… |
| 336 | // TODO probably there needs to be a Polygon constructor for this. | 332 | // TODO probably there needs to be a Polygon constructor for this. |
| 337 | fn polygon<I, T>(c :I) -> Polygon<T> | 333 | fn polygon<I, T>(c :I) -> Polygon<T> |
| 338 | - where I: Iterator<Item = Coordinate<T>> { | ||
| 339 | - Polygon(Coordinates(c.collect())) | 334 | + where I: Iterator<Item = Vertex<T>> { |
| 335 | + Polygon(c.collect()) | ||
| 340 | } | 336 | } |
| 341 | 337 | ||
| 342 | // this one does the projection... as the projection was the last | 338 | // this one does the projection... as the projection was the last |
| 343 | // matrix we do not need to do it here. | 339 | // matrix we do not need to do it here. |
| 344 | let to_coord = |p :&usize| { | 340 | let to_coord = |p :&usize| { |
| 345 | let Point(v, _) = camera.project(self.points[*p]); | 341 | let Point(v, _) = camera.project(self.points[*p]); |
| 346 | - Coordinate(T::round(&v.x()), T::round(&v.y()), v.z() - 1.into()) | 342 | + Vertex::new(T::round(&v.x()), T::round(&v.y()), v.z() - 1.into()) |
| 347 | }; | 343 | }; |
| 348 | let to_poly = |f :&Face<T>| { | 344 | let to_poly = |f :&Face<T>| { |
| 349 | let pg = polygon(f.corners.iter().map(to_coord)); | 345 | let pg = polygon(f.corners.iter().map(to_coord)); |
| 1 | +// | ||
| 2 | +// easel3d is a library that provides basic math for 3D transformation and | ||
| 3 | +// projection as well as a simple software resterizer. | ||
| 4 | +// | ||
| 5 | +// All of them implemented as generics so that they work with f64 and other | ||
| 6 | +// suitable types. | ||
| 7 | +// | ||
| 8 | +// This is mainly the result of my learning rust experiments. So it is | ||
| 9 | +// very likely not optimal and improvements and suggestions are welcome. | ||
| 10 | +// | ||
| 11 | +// The rasterization part, called easel consists of two traits (easel and | ||
| 12 | +// canvas) where the one is cuttently empty because I found no methods that | ||
| 13 | +// I currently need. And the other needs to be implemented to get a | ||
| 14 | +// concrete rasterizer. After that one can use all of the other types there. | ||
| 15 | +// | ||
| 16 | +// Georg Hopp <georg@steffers.org> | ||
| 17 | +// | ||
| 18 | +// Copyright © 2019 Georg Hopp | ||
| 19 | +// | ||
| 20 | +// This program is free software: you can redistribute it and/or modify | ||
| 21 | +// it under the terms of the GNU General Public License as published by | ||
| 22 | +// the Free Software Foundation, either version 3 of the License, or | ||
| 23 | +// (at your option) any later version. | ||
| 24 | +// | ||
| 25 | +// This program is distributed in the hope that it will be useful, | ||
| 26 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 27 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 28 | +// GNU General Public License for more details. | ||
| 29 | +// | ||
| 30 | +// You should have received a copy of the GNU General Public License | ||
| 31 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 32 | +// | ||
| 1 | extern crate lazy_static; | 33 | extern crate lazy_static; |
| 2 | 34 | ||
| 3 | pub type Error = &'static str; | 35 | pub type Error = &'static str; |
| @@ -11,253 +43,3 @@ pub mod geometry; | @@ -11,253 +43,3 @@ pub mod geometry; | ||
| 11 | mod utils; | 43 | mod utils; |
| 12 | 44 | ||
| 13 | use vector::Vector; | 45 | use vector::Vector; |
| 14 | -use easel::{Canvas, Coordinate, Drawable, Fillable}; | ||
| 15 | -use geometry::{Camera, DirectLight, Polyeder, Primitives}; | ||
| 16 | -use transform::{TMatrix}; | ||
| 17 | - | ||
| 18 | -use std::fmt::{Display, Formatter, Result}; | ||
| 19 | -use std::sync::mpsc; | ||
| 20 | -use std::time::Instant; | ||
| 21 | -use wasm_bindgen::prelude::*; | ||
| 22 | - | ||
| 23 | -// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global | ||
| 24 | -// allocator. | ||
| 25 | -#[cfg(feature = "wee_alloc")] | ||
| 26 | -#[global_allocator] | ||
| 27 | -static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; | ||
| 28 | - | ||
| 29 | -#[wasm_bindgen] | ||
| 30 | -#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 31 | -pub struct Color(u8, u8, u8, u8); | ||
| 32 | - | ||
| 33 | -#[wasm_bindgen] | ||
| 34 | -pub struct View3d { width :u16 | ||
| 35 | - , height :u16 | ||
| 36 | - , size :usize | ||
| 37 | - , degree :i32 | ||
| 38 | - //, start :Instant | ||
| 39 | - , tetrahedron :Polyeder<f64> | ||
| 40 | - , cube :Polyeder<f64> | ||
| 41 | - , camera :Option<Camera<f64>> | ||
| 42 | - , light :DirectLight<f64> | ||
| 43 | - , zbuf :Vec<f64> | ||
| 44 | - , image :Vec<Color> | ||
| 45 | -} | ||
| 46 | - | ||
| 47 | -#[wasm_bindgen] | ||
| 48 | -impl View3d { | ||
| 49 | - pub fn new(width :u16, height :u16) -> Self { | ||
| 50 | - let size = width as usize * height as usize; | ||
| 51 | - let light_vector = Vector(0.0, 0.0, 1.0); | ||
| 52 | - | ||
| 53 | - let mut view3d = Self { width: width | ||
| 54 | - , height: height | ||
| 55 | - , size: size | ||
| 56 | - , degree: 0 | ||
| 57 | - //, start: Instant::now() | ||
| 58 | - , tetrahedron: Polyeder::tetrahedron(100.0) | ||
| 59 | - , cube: Polyeder::cube(56.25) | ||
| 60 | - , camera: None | ||
| 61 | - , light: DirectLight::new(light_vector) | ||
| 62 | - , zbuf: vec!(0.0; size) | ||
| 63 | - , image: vec!(Color(0, 0, 0, 0xFF); size) | ||
| 64 | - }; | ||
| 65 | - | ||
| 66 | - view3d.camera = Some(Camera::<f64>::new(&view3d, 45)); | ||
| 67 | - view3d | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - pub fn width(&self) -> u16 { | ||
| 71 | - self.width | ||
| 72 | - } | ||
| 73 | - | ||
| 74 | - pub fn height(&self) -> u16 { | ||
| 75 | - self.height | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - pub fn update(&mut self) { | ||
| 79 | - //let deg = ((self.start.elapsed() / 25).as_millis() % 360) as i32; | ||
| 80 | - let t = TMatrix::translate(Vector(0.0, 0.0, 150.0)); | ||
| 81 | - let rz = TMatrix::rotate_z(self.degree); | ||
| 82 | - let rx = TMatrix::rotate_x(-self.degree*2); | ||
| 83 | - let ry = TMatrix::rotate_y(-self.degree*2); | ||
| 84 | - | ||
| 85 | - let rot1 = TMatrix::combine(vec!(rz, rx, t)); | ||
| 86 | - let rot2 = TMatrix::combine(vec!(rz, ry, t)); | ||
| 87 | - | ||
| 88 | - let objects = vec!( (self.tetrahedron.transform(&rot1), 0xFFFF00) | ||
| 89 | - , ( self.cube.transform(&rot2), 0x0000FF) ); | ||
| 90 | - | ||
| 91 | - self.degree = (self.degree + 1) % 360; | ||
| 92 | - | ||
| 93 | - self.clear(); | ||
| 94 | - | ||
| 95 | - match self.camera { | ||
| 96 | - None => {}, | ||
| 97 | - Some(camera) => { | ||
| 98 | - for (o, color) in objects { | ||
| 99 | - for (pg, c) in o.project(&camera, &self.light, color) { | ||
| 100 | - (&pg).fill(self, c); | ||
| 101 | - } | ||
| 102 | - } | ||
| 103 | - }, | ||
| 104 | - } | ||
| 105 | - } | ||
| 106 | - | ||
| 107 | - pub fn image(&self) -> *const Color { | ||
| 108 | - self.image.as_ptr() | ||
| 109 | - } | ||
| 110 | -} | ||
| 111 | - | ||
| 112 | -impl Canvas<f64> for View3d { | ||
| 113 | - fn width(&self) -> u16 { | ||
| 114 | - self.width | ||
| 115 | - } | ||
| 116 | - | ||
| 117 | - fn height(&self) -> u16 { | ||
| 118 | - self.height | ||
| 119 | - } | ||
| 120 | - | ||
| 121 | - fn clear(&mut self) { | ||
| 122 | - self.zbuf = vec!(0.0; self.size); | ||
| 123 | - self.image = vec!(Color(0, 0, 0, 0xFF); self.size); | ||
| 124 | - } | ||
| 125 | - | ||
| 126 | - fn set_pixel(&mut self, c :Coordinate<f64>, color :u32) { | ||
| 127 | - let Coordinate(x, y, zr) = c; | ||
| 128 | - let idx :usize = (y * (self.width as i32) + x) as usize; | ||
| 129 | - | ||
| 130 | - let r = ((color >> 16) & 0xFF) as u8; | ||
| 131 | - let g = ((color >> 8) & 0xFF) as u8; | ||
| 132 | - let b = ( color & 0xFF) as u8; | ||
| 133 | - | ||
| 134 | - if self.zbuf[idx] < zr { | ||
| 135 | - self.zbuf[idx] = zr; | ||
| 136 | - self.image[idx] = Color(r, g, b, 0xFF); | ||
| 137 | - } | ||
| 138 | - } | ||
| 139 | - | ||
| 140 | - // Empty implementations for now… mostly not needed because it is | ||
| 141 | - // done from JavaScript… | ||
| 142 | - fn init_events(&self) {} | ||
| 143 | - fn start_events(&self, _ :mpsc::Sender<i32>) {} | ||
| 144 | - fn draw( &mut self, _ :&dyn Drawable<f64>, _ :Coordinate<f64>, _ :u32 ) {} | ||
| 145 | - fn put_text(&self, _ :Coordinate<f64>, _ :&str) {} | ||
| 146 | - fn show(&self) {} | ||
| 147 | -} | ||
| 148 | - | ||
| 149 | -#[wasm_bindgen] | ||
| 150 | -#[repr(u8)] | ||
| 151 | -#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 152 | -pub enum Cell { | ||
| 153 | - Dead = 0, | ||
| 154 | - Alive = 1, | ||
| 155 | -} | ||
| 156 | - | ||
| 157 | -#[wasm_bindgen] | ||
| 158 | -pub struct Universe { | ||
| 159 | - width :u32, | ||
| 160 | - height :u32, | ||
| 161 | - cells :Vec<Cell>, | ||
| 162 | -} | ||
| 163 | - | ||
| 164 | -#[wasm_bindgen] | ||
| 165 | -impl Universe { | ||
| 166 | - pub fn new() -> Universe { | ||
| 167 | - let width = 64; | ||
| 168 | - let height = 64; | ||
| 169 | - | ||
| 170 | - let init_cells = |i :u32| { | ||
| 171 | - if i % 2 == 0 || i % 7 == 0 { Cell::Alive } else { Cell::Dead } | ||
| 172 | - }; | ||
| 173 | - | ||
| 174 | - let cells = (0..width * height).map(init_cells).collect(); | ||
| 175 | - | ||
| 176 | - Universe { | ||
| 177 | - width: width, | ||
| 178 | - height: height, | ||
| 179 | - cells: cells, | ||
| 180 | - } | ||
| 181 | - } | ||
| 182 | - | ||
| 183 | - pub fn width(&self) -> u32 { | ||
| 184 | - self.width | ||
| 185 | - } | ||
| 186 | - | ||
| 187 | - pub fn height(&self) -> u32 { | ||
| 188 | - self.height | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - pub fn cells(&self) -> *const Cell { | ||
| 192 | - self.cells.as_ptr() | ||
| 193 | - } | ||
| 194 | - | ||
| 195 | - pub fn render(&self) -> String { | ||
| 196 | - self.to_string() | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - pub fn tick(&mut self) { | ||
| 200 | - let mut next = self.cells.clone(); | ||
| 201 | - | ||
| 202 | - for row in 0..self.height { | ||
| 203 | - for col in 0..self.width { | ||
| 204 | - let idx = self.get_index(row, col); | ||
| 205 | - let cell = self.cells[idx]; | ||
| 206 | - let live_neighbors = self.live_neighbor_count(row, col); | ||
| 207 | - | ||
| 208 | - // Game of life rules.... | ||
| 209 | - let next_cell = match (cell, live_neighbors) { | ||
| 210 | - (Cell::Alive, 2) | | ||
| 211 | - (Cell::Alive, 3) => Cell::Alive, | ||
| 212 | - (Cell::Alive, _) => Cell::Dead, | ||
| 213 | - ( Cell::Dead, 3) => Cell::Alive, | ||
| 214 | - ( otherwise, _) => otherwise, | ||
| 215 | - }; | ||
| 216 | - | ||
| 217 | - next[idx] = next_cell; | ||
| 218 | - } | ||
| 219 | - } | ||
| 220 | - | ||
| 221 | - self.cells = next; | ||
| 222 | - } | ||
| 223 | - | ||
| 224 | - fn get_index(&self, row :u32, col :u32) -> usize { | ||
| 225 | - (row * self.width + col) as usize | ||
| 226 | - } | ||
| 227 | - | ||
| 228 | - fn live_neighbor_count(&self, row :u32, col :u32) -> u8 { | ||
| 229 | - let mut count = 0; | ||
| 230 | - | ||
| 231 | - for delta_row in [self.height - 1, 0, 1].iter().cloned() { | ||
| 232 | - for delta_col in [self.width - 1, 0, 1].iter().cloned() { | ||
| 233 | - if delta_row == 0 && delta_col == 0 { | ||
| 234 | - continue; | ||
| 235 | - } | ||
| 236 | - | ||
| 237 | - let neighbor_row = (row + delta_row) % self.height; | ||
| 238 | - let neighbor_col = (col + delta_col) % self.width; | ||
| 239 | - let idx = self.get_index(neighbor_row, neighbor_col); | ||
| 240 | - count += self.cells[idx] as u8; | ||
| 241 | - } | ||
| 242 | - } | ||
| 243 | - | ||
| 244 | - count | ||
| 245 | - } | ||
| 246 | -} | ||
| 247 | - | ||
| 248 | -impl Display for Universe { | ||
| 249 | - fn fmt(&self, f :&mut Formatter) -> Result { | ||
| 250 | - for line in self.cells.as_slice().chunks(self.width as usize) { | ||
| 251 | - for &cell in line { | ||
| 252 | - let symbol = match cell { | ||
| 253 | - Cell::Dead => ' ', | ||
| 254 | - Cell::Alive => '*', | ||
| 255 | - }; | ||
| 256 | - write!(f, "{}", symbol)?; | ||
| 257 | - } | ||
| 258 | - write!(f, "\n")?; | ||
| 259 | - } | ||
| 260 | - | ||
| 261 | - Ok(()) | ||
| 262 | - } | ||
| 263 | -} |
src/utils.rs
deleted
100644 → 0
| 1 | -pub fn set_panic_hook() { | ||
| 2 | - // When the `console_error_panic_hook` feature is enabled, we can call the | ||
| 3 | - // `set_panic_hook` function at least once during initialization, and then | ||
| 4 | - // we will get better error messages if our code ever panics. | ||
| 5 | - // | ||
| 6 | - // For more details see | ||
| 7 | - // https://github.com/rustwasm/console_error_panic_hook#readme | ||
| 8 | - #[cfg(feature = "console_error_panic_hook")] | ||
| 9 | - console_error_panic_hook::set_once(); | ||
| 10 | -} |
Please
register
or
login
to post a comment