Skip to content

Commit 142ab84

Browse files
committed
Move geometrify into its own module.
1 parent 939e9d5 commit 142ab84

File tree

3 files changed

+341
-337
lines changed

3 files changed

+341
-337
lines changed

src/bin/cli.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ extern crate clap;
44

55

66
use clap::{Arg, App, AppSettings};
7-
use geometrify::{RandomPointGenerator, Geometrify};
7+
8+
use geometrify::RandomPointGenerator;
9+
use geometrify::geometrify::Geometrify;
810

911
use image::open;
1012
use std::path::Path;

src/geometrify.rs

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
use super::{Point, BoundingBox, PointGenerator};
2+
use image::{Rgba, RgbaImage, Pixel};
3+
4+
use pbr::ProgressBar;
5+
6+
use rayon::prelude::*;
7+
8+
trait Primitive {
9+
fn is_inside_primitive(&self, p: Point) -> bool;
10+
fn bounding_box(&self) -> BoundingBox;
11+
fn get_color(&self) -> Option<Rgba<u8>>;
12+
fn set_color(&mut self, color: Rgba<u8>);
13+
}
14+
15+
#[derive(Debug, Copy, Clone)]
16+
struct Triangle {
17+
a: Point,
18+
b: Point,
19+
c: Point,
20+
color: Option<Rgba<u8>>,
21+
22+
span_div: Option<f32>,
23+
}
24+
25+
impl Triangle {
26+
fn new(a: Point, b: Point, c: Point) -> Triangle {
27+
Triangle {
28+
a: a,
29+
b: b,
30+
c: c,
31+
color: None,
32+
span_div: None,
33+
}
34+
}
35+
36+
fn span_div_save(&mut self) {
37+
self.span_div = Some(self.span_div());
38+
}
39+
40+
fn span_div(&self) -> f32 {
41+
match self.span_div {
42+
Some(div) => div,
43+
None => {
44+
let span_a = Point {
45+
x: self.b.x - self.a.x,
46+
y: self.b.y - self.a.y,
47+
};
48+
let span_b = Point {
49+
x: self.c.x - self.a.x,
50+
y: self.c.y - self.a.y,
51+
};
52+
53+
1.0 / span_a.cross_product(span_b) as f32
54+
}
55+
}
56+
}
57+
}
58+
59+
impl Primitive for Triangle {
60+
fn is_inside_primitive(&self, p: Point) -> bool {
61+
let span_a = Point {
62+
x: self.b.x - self.a.x,
63+
y: self.b.y - self.a.y,
64+
};
65+
let span_b = Point {
66+
x: self.c.x - self.a.x,
67+
y: self.c.y - self.a.y,
68+
};
69+
70+
let q = Point {
71+
x: p.x - self.a.x,
72+
y: p.y - self.a.y,
73+
};
74+
75+
let s = q.cross_product(span_b) as f32 * self.span_div();
76+
let t = span_a.cross_product(q) as f32 * self.span_div();
77+
78+
(s >= 0.0) && (t >= 0.0) && ((s + t) <= 1.0)
79+
}
80+
81+
fn bounding_box(&self) -> BoundingBox {
82+
use std::cmp::{min, max};
83+
BoundingBox {
84+
top_left: Point {
85+
x: min(min(self.a.x, self.b.x), self.c.x),
86+
y: min(min(self.a.y, self.b.y), self.c.y),
87+
},
88+
bottom_right: Point {
89+
x: max(max(self.a.x, self.b.x), self.c.x),
90+
y: max(max(self.a.y, self.b.y), self.c.y),
91+
},
92+
}
93+
}
94+
95+
fn get_color(&self) -> Option<Rgba<u8>> {
96+
self.color
97+
}
98+
99+
fn set_color(&mut self, color: Rgba<u8>) {
100+
self.color = Some(color);
101+
}
102+
}
103+
104+
pub struct Geometrify {
105+
point_gen: Box<PointGenerator>,
106+
iterations: u32,
107+
samples: u32,
108+
}
109+
110+
impl Geometrify {
111+
pub fn new(point_gen: Box<PointGenerator>, iterations: u32, samples: u32) -> Geometrify {
112+
Geometrify {
113+
point_gen: point_gen,
114+
iterations: iterations,
115+
samples: samples,
116+
}
117+
}
118+
119+
pub fn set_iterations(&mut self, iterations: u32) {
120+
self.iterations = iterations
121+
}
122+
123+
pub fn set_samples(&mut self, samples: u32) {
124+
self.samples = samples
125+
}
126+
127+
fn calculate_color(image: &RgbaImage, primitive: &Primitive) -> Rgba<u8> {
128+
let bb = primitive.bounding_box();
129+
130+
let mut count = 0u64;
131+
let mut cr = 0u64;
132+
let mut cg = 0u64;
133+
let mut cb = 0u64;
134+
let mut ca = 0u64;
135+
136+
for y in bb.top_left.y..bb.bottom_right.y {
137+
for x in bb.top_left.x..bb.bottom_right.x {
138+
let (r, g, b, a) = image.get_pixel(x as u32, y as u32).channels4();
139+
cr += r as u64;
140+
cg += g as u64;
141+
cb += b as u64;
142+
ca += a as u64;
143+
count += 1;
144+
}
145+
}
146+
147+
if count == 0 {
148+
Rgba::from_channels(255, 255, 255, 255)
149+
} else {
150+
Rgba::from_channels(
151+
(cr / count) as u8,
152+
(cg / count) as u8,
153+
(cb / count) as u8,
154+
(ca / count) as u8,
155+
)
156+
}
157+
}
158+
159+
fn generate_primitive(&mut self, width: u32, height: u32) -> Triangle {
160+
Triangle::new(
161+
self.point_gen.next_point(width, height),
162+
self.point_gen.next_point(width, height),
163+
self.point_gen.next_point(width, height),
164+
)
165+
}
166+
167+
fn add_to_image(image: &mut RgbaImage, primitive: &Primitive) {
168+
let bb = primitive.bounding_box();
169+
170+
for y in bb.top_left.y..bb.bottom_right.y {
171+
for x in bb.top_left.x..bb.bottom_right.x {
172+
let p = Point {
173+
x: x as i32,
174+
y: y as i32,
175+
};
176+
if primitive.is_inside_primitive(p) {
177+
*image.get_pixel_mut(x as u32, y as u32) =
178+
Geometrify::mix_color(
179+
primitive.get_color().expect("color of triangle not set"),
180+
*image.get_pixel(x as u32, y as u32),
181+
);
182+
}
183+
}
184+
}
185+
}
186+
187+
fn mix_color(first: Rgba<u8>, second: Rgba<u8>) -> Rgba<u8> {
188+
let (r1, g1, b1, a1) = first.channels4();
189+
let (r2, g2, b2, a2) = second.channels4();
190+
191+
Rgba::from_channels(
192+
(((r1 as u32 + r2 as u32) / 2) as u8),
193+
(((g1 as u32 + g2 as u32) / 2) as u8),
194+
(((b1 as u32 + b2 as u32) / 2) as u8),
195+
(((a1 as u32 + a2 as u32) / 2) as u8),
196+
)
197+
}
198+
199+
fn difference(first: Rgba<u8>, second: Rgba<u8>) -> u32 {
200+
let (r1, g1, b1, a1) = first.channels4();
201+
let (r2, g2, b2, a2) = second.channels4();
202+
let mut d = 0i32;
203+
204+
d += i32::abs(r1 as i32 - r2 as i32);
205+
d += i32::abs(g1 as i32 - g2 as i32);
206+
d += i32::abs(b1 as i32 - b2 as i32);
207+
d += i32::abs(a1 as i32 - a2 as i32);
208+
209+
d as u32
210+
}
211+
212+
fn calculate_difference(original: &RgbaImage,
213+
current: &RgbaImage,
214+
diff_lut: &[u64],
215+
primitive: &Primitive)
216+
-> u64 {
217+
let bb = primitive.bounding_box();
218+
219+
// Use LUT to calculate difference outside of the BB
220+
// TODO: Check whether indices are correct!
221+
let mut d = diff_lut[diff_lut.len() - 1];
222+
if bb.bottom_right.y > 0 && bb.bottom_right.x > 0 {
223+
d -= diff_lut[((bb.bottom_right.y - 1) as u32 * current.width() +
224+
bb.bottom_right.x as u32 - 1) as usize];
225+
}
226+
if bb.top_left.y > 0 && bb.bottom_right.x > 0 {
227+
d += diff_lut[((bb.top_left.y - 1) as u32 * current.width() +
228+
bb.bottom_right.x as u32 - 1) as usize];
229+
}
230+
if bb.bottom_right.y > 0 && bb.top_left.x > 0 {
231+
d += diff_lut[((bb.bottom_right.y - 1) as u32 * current.width() +
232+
bb.top_left.x as u32 - 1) as usize];
233+
}
234+
if bb.top_left.y > 0 && bb.top_left.x > 0 {
235+
d -= diff_lut[((bb.top_left.y - 1) as u32 * current.width() +
236+
bb.top_left.x as u32 - 1) as usize];
237+
}
238+
239+
for y in bb.top_left.y..bb.bottom_right.y {
240+
for x in bb.top_left.x..bb.bottom_right.x {
241+
let original_rgb = original.get_pixel(x as u32, y as u32);
242+
let result_rgb = if (bb.top_left.x as u32 <= x as u32) &&
243+
(x as u32 <= bb.bottom_right.x as u32) &&
244+
(bb.top_left.y as u32 <= y as u32) &&
245+
(y as u32 <= bb.bottom_right.y as u32) &&
246+
(primitive.is_inside_primitive(
247+
Point {
248+
x: x as i32,
249+
y: y as i32,
250+
}
251+
)) {
252+
Geometrify::mix_color(
253+
*current.get_pixel(x as u32, y as u32),
254+
primitive.get_color().expect("triangle color not "),
255+
)
256+
} else {
257+
*current.get_pixel(x as u32, y as u32)
258+
};
259+
260+
d += Geometrify::difference(*original_rgb, result_rgb) as u64;
261+
}
262+
}
263+
264+
d
265+
}
266+
267+
fn calculate_difference_lut(a: &RgbaImage, b: &RgbaImage) -> Vec<u64> {
268+
let mut result = Vec::new();
269+
270+
for y in 0..a.height() {
271+
for x in 0..a.width() {
272+
let mut ldiff = Geometrify::difference(*a.get_pixel(x, y), *b.get_pixel(x, y)) as
273+
u64;
274+
if x > 0 {
275+
ldiff += result[(y * a.width() + x - 1) as usize];
276+
}
277+
if y > 0 {
278+
ldiff += result[((y - 1) * a.width() + x) as usize];
279+
}
280+
if x > 0 && y > 0 {
281+
ldiff -= result[((y - 1) * a.width() + x - 1) as usize];
282+
}
283+
284+
result.push(ldiff);
285+
}
286+
}
287+
288+
result
289+
}
290+
291+
pub fn apply(&mut self, image: RgbaImage) -> RgbaImage {
292+
let mut progress = ProgressBar::new(self.iterations as u64);
293+
progress.format("|#--|");
294+
295+
let mut destination = RgbaImage::new(image.width(), image.height());
296+
297+
for _ in 0..self.iterations {
298+
let difference_lut = Geometrify::calculate_difference_lut(&image, &destination);
299+
300+
let primitives = (0..self.samples)
301+
.map(|_| self.generate_primitive(image.width(), image.height()))
302+
.map(
303+
|mut p| {
304+
p.span_div_save();
305+
p
306+
}
307+
)
308+
.collect::<Vec<Triangle>>();
309+
let min_primitive = primitives
310+
.par_iter()
311+
.map(
312+
|primitive| {
313+
let mut prim = *primitive;
314+
prim.color = Some(Geometrify::calculate_color(&image, &prim));
315+
(prim,
316+
Geometrify::calculate_difference(
317+
&image,
318+
&destination,
319+
&difference_lut,
320+
&prim,
321+
))
322+
}
323+
)
324+
.min_by_key(|tup| tup.1);
325+
326+
Geometrify::add_to_image(
327+
&mut destination,
328+
&min_primitive.expect("no fitting triangle found").0,
329+
);
330+
progress.inc();
331+
}
332+
progress.finish_print("done");
333+
334+
destination
335+
}
336+
}

0 commit comments

Comments
 (0)