aboutsummaryrefslogtreecommitdiff
path: root/src/rasterizer/path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/rasterizer/path.rs')
-rw-r--r--src/rasterizer/path.rs62
1 files changed, 49 insertions, 13 deletions
diff --git a/src/rasterizer/path.rs b/src/rasterizer/path.rs
index b2db356..35ae604 100644
--- a/src/rasterizer/path.rs
+++ b/src/rasterizer/path.rs
@@ -1,5 +1,6 @@
use crate::BackendCoord;
+// Compute the tanginal and normal vectors of the given straight line.
fn get_dir_vector(from: BackendCoord, to: BackendCoord, flag: bool) -> ((f64, f64), (f64, f64)) {
let v = (i64::from(to.0 - from.0), i64::from(to.1 - from.1));
let l = ((v.0 * v.0 + v.1 * v.1) as f64).sqrt();
@@ -13,10 +14,17 @@ fn get_dir_vector(from: BackendCoord, to: BackendCoord, flag: bool) -> ((f64, f6
}
}
-fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord {
+// Compute the polygonized vertex of the given angle
+// d is the distance between the polygon edge and the actual line.
+// d can be negative, this will emit a vertex on the other side of the line.
+fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64, buf: &mut Vec<BackendCoord>) {
+ buf.clear();
+
+ // Compute the tanginal and normal vectors of the given straight line.
let (a_t, a_n) = get_dir_vector(triple[0], triple[1], false);
let (b_t, b_n) = get_dir_vector(triple[2], triple[1], true);
+ // Compute a point that is d away from the line for line a and line b.
let a_p = (
f64::from(triple[1].0) + d * a_n.0,
f64::from(triple[1].1) + d * a_n.1,
@@ -26,12 +34,25 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord {
f64::from(triple[1].1) + d * b_n.1,
);
+ // If they are actually the same point, then the 3 points are colinear, so just emit the point.
+ if a_p.0 as i32 == b_p.0 as i32 && a_p.1 as i32 == b_p.1 as i32 {
+ buf.push((a_p.0 as i32, a_p.1 as i32));
+ return;
+ }
+
+ // So we are actually computing the intersection of two lines:
+ // a_p + u * a_t and b_p + v * b_t.
+ // We can solve the following vector equation:
// u * a_t + a_p = v * b_t + b_p
+ //
+ // which is actually a equation system:
// u * a_t.0 - v * b_t.0 = b_p.0 - a_p.0
// u * a_t.1 - v * b_t.1 = b_p.1 - a_p.1
- if a_p.0 as i32 == b_p.0 as i32 && a_p.1 as i32 == b_p.1 as i32 {
- return (a_p.0 as i32, a_p.1 as i32);
- }
+
+ // The following vars are coefficients of the linear equation system.
+ // a0*u + b0*v = c0
+ // a1*u + b1*v = c1
+ // in which x and y are the coordinates that two polygon edges intersect.
let a0 = a_t.0;
let b0 = -b_t.0;
@@ -40,17 +61,30 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord {
let b1 = -b_t.1;
let c1 = b_p.1 - a_p.1;
- // This is the coner case that
- if (a0 * b1 - a1 * b0).abs() < 1e-10 {
- return (a_p.0 as i32, a_p.1 as i32);
- }
+ let mut x = f64::INFINITY;
+ let mut y = f64::INFINITY;
+
+ // Well if the determinant is not 0, then we can actuall get a intersection point.
+ if (a0 * b1 - a1 * b0).abs() > f64::EPSILON {
+ let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0);
- let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0);
+ x = a_p.0 + u * a_t.0;
+ y = a_p.1 + u * a_t.1;
+ }
- let x = a_p.0 + u * a_t.0;
- let y = a_p.1 + u * a_t.1;
+ let cross_product = a_t.0 * b_t.1 - a_t.1 * b_t.0;
+ if (cross_product < 0.0 && d < 0.0) || (cross_product > 0.0 && d > 0.0) {
+ // Then we are at the outter side of the angle, so we need to consider a cap.
+ let dist_square = (x - triple[1].0 as f64).powi(2) + (y - triple[1].1 as f64).powi(2);
+ // If the point is too far away from the line, we need to cap it.
+ if dist_square > d * d * 16.0 {
+ buf.push((a_p.0.round() as i32, a_p.1.round() as i32));
+ buf.push((b_p.0.round() as i32, b_p.1.round() as i32));
+ return;
+ }
+ }
- (x.round() as i32, y.round() as i32)
+ buf.push((x.round() as i32, y.round() as i32));
}
fn traverse_vertices<'a>(
@@ -78,6 +112,7 @@ fn traverse_vertices<'a>(
));
let mut recent = [(0, 0), *a, *b];
+ let mut vertex_buf = Vec::with_capacity(3);
for p in vertices {
if *p == recent[2] {
@@ -86,7 +121,8 @@ fn traverse_vertices<'a>(
recent.swap(0, 1);
recent.swap(1, 2);
recent[2] = *p;
- op(compute_polygon_vertex(&recent, f64::from(width) / 2.0));
+ compute_polygon_vertex(&recent, f64::from(width) / 2.0, &mut vertex_buf);
+ vertex_buf.iter().cloned().for_each(&mut op);
}
let b = recent[1];