Skip to content

Commit a1cdb43

Browse files
committed
Fix bug #347
This bug was caused by the default rasterizer plolygonize a path line with stroke width later than 1. When angle is too small, the polygon vertex might be very far away from the center line. In SVG rasterizer, this vertex will be dropped - but our rasterizer lack of this type of check. This change implemented the check, when distance between the line vertex the coresponding plolygon vertex on the outter side is larger than 2 times of the stroke width - Then we cap the polygon.
1 parent bed6c08 commit a1cdb43

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

plotters-backend/src/rasterizer/path.rs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::BackendCoord;
22

3+
// Compute the tanginal and normal vectors of the given straight line.
34
fn get_dir_vector(from: BackendCoord, to: BackendCoord, flag: bool) -> ((f64, f64), (f64, f64)) {
45
let v = (i64::from(to.0 - from.0), i64::from(to.1 - from.1));
56
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
1314
}
1415
}
1516

16-
fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord {
17+
// Compute the polygonized vertex of the given angle
18+
// d is the distance between the polygon edge and the actual line.
19+
// d can be negative, this will emit a vertex on the other side of the line.
20+
fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64, buf: &mut Vec<BackendCoord>) {
21+
buf.clear();
22+
23+
// Compute the tanginal and normal vectors of the given straight line.
1724
let (a_t, a_n) = get_dir_vector(triple[0], triple[1], false);
1825
let (b_t, b_n) = get_dir_vector(triple[2], triple[1], true);
1926

27+
// Compute a point that is d away from the line for line a and line b.
2028
let a_p = (
2129
f64::from(triple[1].0) + d * a_n.0,
2230
f64::from(triple[1].1) + d * a_n.1,
@@ -26,12 +34,25 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord {
2634
f64::from(triple[1].1) + d * b_n.1,
2735
);
2836

37+
// If they are actually the same point, then the 3 points are colinear, so just emit the point.
38+
if a_p.0 as i32 == b_p.0 as i32 && a_p.1 as i32 == b_p.1 as i32 {
39+
buf.push((a_p.0 as i32, a_p.1 as i32));
40+
return;
41+
}
42+
43+
// So we are actually computing the intersection of two lines:
44+
// a_p + u * a_t and b_p + v * b_t.
45+
// We can solve the following vector equation:
2946
// u * a_t + a_p = v * b_t + b_p
47+
//
48+
// which is actually a equation system:
3049
// u * a_t.0 - v * b_t.0 = b_p.0 - a_p.0
3150
// u * a_t.1 - v * b_t.1 = b_p.1 - a_p.1
32-
if a_p.0 as i32 == b_p.0 as i32 && a_p.1 as i32 == b_p.1 as i32 {
33-
return (a_p.0 as i32, a_p.1 as i32);
34-
}
51+
52+
// The following vars are coefficients of the linear equation system.
53+
// a0*u + b0*v = c0
54+
// a1*u + b1*v = c1
55+
// in which x and y are the coordinates that two polygon edges intersect.
3556

3657
let a0 = a_t.0;
3758
let b0 = -b_t.0;
@@ -40,17 +61,30 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord {
4061
let b1 = -b_t.1;
4162
let c1 = b_p.1 - a_p.1;
4263

43-
// This is the coner case that
44-
if (a0 * b1 - a1 * b0).abs() < 1e-10 {
45-
return (a_p.0 as i32, a_p.1 as i32);
46-
}
64+
let mut x = f64::INFINITY;
65+
let mut y = f64::INFINITY;
66+
67+
// Well if the determinant is not 0, then we can actuall get a intersection point.
68+
if (a0 * b1 - a1 * b0).abs() > f64::EPSILON {
69+
let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0);
4770

48-
let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0);
71+
x = a_p.0 + u * a_t.0;
72+
y = a_p.1 + u * a_t.1;
73+
}
4974

50-
let x = a_p.0 + u * a_t.0;
51-
let y = a_p.1 + u * a_t.1;
75+
let cross_product = a_t.0 * b_t.1 - a_t.1 * b_t.0;
76+
if (cross_product < 0.0 && d < 0.0) || (cross_product > 0.0 && d > 0.0) {
77+
// Then we are at the outter side of the angle, so we need to consider a cap.
78+
let dist_square = (x - triple[1].0 as f64).powi(2) + (y - triple[1].1 as f64).powi(2);
79+
// If the point is too far away from the line, we need to cap it.
80+
if dist_square > d * d * 16.0 {
81+
buf.push((a_p.0.round() as i32, a_p.1.round() as i32));
82+
buf.push((b_p.0.round() as i32, b_p.1.round() as i32));
83+
return;
84+
}
85+
}
5286

53-
(x.round() as i32, y.round() as i32)
87+
buf.push((x.round() as i32, y.round() as i32));
5488
}
5589

5690
fn traverse_vertices<'a>(
@@ -78,6 +112,7 @@ fn traverse_vertices<'a>(
78112
));
79113

80114
let mut recent = [(0, 0), *a, *b];
115+
let mut vertex_buf = Vec::with_capacity(3);
81116

82117
for p in vertices {
83118
if *p == recent[2] {
@@ -86,7 +121,8 @@ fn traverse_vertices<'a>(
86121
recent.swap(0, 1);
87122
recent.swap(1, 2);
88123
recent[2] = *p;
89-
op(compute_polygon_vertex(&recent, f64::from(width) / 2.0));
124+
compute_polygon_vertex(&recent, f64::from(width) / 2.0, &mut vertex_buf);
125+
vertex_buf.iter().cloned().for_each(|v| op(v));
90126
}
91127

92128
let b = recent[1];

plotters-bitmap/src/bitmap/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ fn test_bitmap_blend_large() {
225225
#[test]
226226
fn test_bitmap_bgrx_pixel_format() {
227227
use crate::{bitmap_pixel::BGRXPixel, BitMapBackend};
228-
use plotters::prelude::{BLACK, RED, WHITE, GREEN, YELLOW, BLUE, RGBColor, Color};
228+
use plotters::prelude::{Color, RGBColor, BLACK, BLUE, GREEN, RED, WHITE, YELLOW};
229229
let mut rgb_buffer = vec![0; 1000 * 1000 * 3];
230230
let mut bgrx_buffer = vec![0; 1000 * 1000 * 4];
231231

plotters-svg/src/svg.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,11 @@ impl Drop for SVGBackend<'_> {
578578
mod test {
579579
use super::*;
580580
use plotters::element::Circle;
581+
use plotters::prelude::{
582+
ChartBuilder, Color, IntoDrawingArea, IntoFont, SeriesLabelPosition, TextStyle, BLACK,
583+
BLUE, RED, WHITE,
584+
};
581585
use plotters::style::text_anchor::{HPos, Pos, VPos};
582-
use plotters::prelude::{ChartBuilder, IntoDrawingArea, TextStyle, IntoFont, BLACK, Color, RED, BLUE, SeriesLabelPosition, WHITE};
583586
use std::fs;
584587
use std::path::Path;
585588

0 commit comments

Comments
 (0)