@@ -14,23 +14,13 @@ fn get_dir_vector(from: BackendCoord, to: BackendCoord, flag: bool) -> ((f64, f6
14
14
}
15
15
}
16
16
17
- /// Consider two line segments between three points: t1-t2-t3
18
- /// Imagine the line bends to the right, and we run along the line on the right hand side.
19
- /// In that case, we make an "inside corner" at t2.
20
- /// This function would compute the point close to t2 that makes the line have thickness `d`
21
- ///
22
- /// For "outside corners" (the line bends to the left), we sometimes get too pointy corners
23
- /// if there is just one new point. In that case, we add a cap to make the corner look better.
24
- /// In that case, the function emits two points.
25
- ///
26
- /// The function will return values via the `buf` parameter, after clearing it.
27
- ///
28
- /// d can be negative, this will emit a vertex on the other side of the line.
29
- ///
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.
30
20
fn compute_polygon_vertex ( triple : & [ BackendCoord ; 3 ] , d : f64 , buf : & mut Vec < BackendCoord > ) {
31
21
buf. clear ( ) ;
32
22
33
- // Compute the tangential and normal vectors of the given straight line.
23
+ // Compute the tanginal and normal vectors of the given straight line.
34
24
let ( a_t, a_n) = get_dir_vector ( triple[ 0 ] , triple[ 1 ] , false ) ;
35
25
let ( b_t, b_n) = get_dir_vector ( triple[ 2 ] , triple[ 1 ] , true ) ;
36
26
@@ -44,7 +34,13 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64, buf: &mut Vec<Back
44
34
f64:: from ( triple[ 1 ] . 1 ) + d * b_n. 1 ,
45
35
) ;
46
36
47
- // We want to compute the intersection of two lines:
37
+ // Check if 3 points are colinear, up to precision. If so, just emit the point.
38
+ if ( a_t. 1 * b_t. 0 - a_t. 0 * b_t. 1 ) . abs ( ) <= f64:: EPSILON {
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:
48
44
// a_p + u * a_t and b_p + v * b_t.
49
45
// We can solve the following vector equation:
50
46
// u * a_t + a_p = v * b_t + b_p
@@ -65,38 +61,24 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64, buf: &mut Vec<Back
65
61
let b1 = -b_t. 1 ;
66
62
let c1 = b_p. 1 - a_p. 1 ;
67
63
68
- // If the determinant is 0, then we cannot actually get an intersection point.
69
- // In that case, the two lines are parallel and we just emit the point a_p, which is
70
- // approximately equal to b_p
71
- if ( a0 * b1 - a1 * b0) . abs ( ) <= f64:: EPSILON {
72
- buf. push ( ( a_p. 0 as i32 , a_p. 1 as i32 ) ) ;
73
- return ;
74
- } else {
75
- let u = ( c0 * b1 - c1 * b0) / ( a0 * b1 - a1 * b0) ;
76
- let x = a_p. 0 + u * a_t. 0 ;
77
- let y = a_p. 1 + u * a_t. 1 ;
78
-
79
- let cross_product = a_t. 0 * b_t. 1 - a_t. 1 * b_t. 0 ;
80
- let is_outside_the_angle =
81
- ( cross_product < 0.0 && d < 0.0 ) || ( cross_product > 0.0 && d > 0.0 ) ;
82
- if is_outside_the_angle {
83
- // We are at the outer side of the angle, so we need to consider a cap.
84
- let dist_square = ( x - triple[ 1 ] . 0 as f64 ) . powi ( 2 ) + ( y - triple[ 1 ] . 1 as f64 ) . powi ( 2 ) ;
85
- let needs_capping = dist_square > d * d * 16.0 ;
86
- if needs_capping {
87
- // If the point is too far away from the line, we need to cap it to make it look okay
88
- buf. push ( ( a_p. 0 . round ( ) as i32 , a_p. 1 . round ( ) as i32 ) ) ;
89
- buf. push ( ( b_p. 0 . round ( ) as i32 , b_p. 1 . round ( ) as i32 ) ) ;
90
- return ;
91
- } else {
92
- // We are at the outer side of the angle, at an appropriate distance, so we just emit the point.
93
- buf. push ( ( x. round ( ) as i32 , y. round ( ) as i32 ) ) ;
94
- }
95
- } else {
96
- // We are at the inner side of the angle, so we just emit the point.
97
- buf. push ( ( x. round ( ) as i32 , y. round ( ) as i32 ) ) ;
64
+ // Since the points are not collinear, the determinant is not 0, and we can get a intersection point.
65
+ let u = ( c0 * b1 - c1 * b0) / ( a0 * b1 - a1 * b0) ;
66
+ let x = a_p. 0 + u * a_t. 0 ;
67
+ let y = a_p. 1 + u * a_t. 1 ;
68
+
69
+ let cross_product = a_t. 0 * b_t. 1 - a_t. 1 * b_t. 0 ;
70
+ if ( cross_product < 0.0 && d < 0.0 ) || ( cross_product > 0.0 && d > 0.0 ) {
71
+ // Then we are at the outer side of the angle, so we need to consider a cap.
72
+ let dist_square = ( x - triple[ 1 ] . 0 as f64 ) . powi ( 2 ) + ( y - triple[ 1 ] . 1 as f64 ) . powi ( 2 ) ;
73
+ // If the point is too far away from the line, we need to cap it.
74
+ if dist_square > d * d * 16.0 {
75
+ buf. push ( ( a_p. 0 . round ( ) as i32 , a_p. 1 . round ( ) as i32 ) ) ;
76
+ buf. push ( ( b_p. 0 . round ( ) as i32 , b_p. 1 . round ( ) as i32 ) ) ;
77
+ return ;
98
78
}
99
79
}
80
+
81
+ buf. push ( ( x. round ( ) as i32 , y. round ( ) as i32 ) ) ;
100
82
}
101
83
102
84
fn traverse_vertices < ' a > (
@@ -162,11 +144,11 @@ pub fn polygonize(vertices: &[BackendCoord], stroke_width: u32) -> Vec<BackendCo
162
144
ret
163
145
}
164
146
165
-
166
147
#[ cfg( test) ]
167
148
mod test
168
149
{
169
150
use super :: * ;
151
+
170
152
/// Test for regression with respect to https://github.com/plotters-rs/plotters/issues/562
171
153
#[ test]
172
154
fn test_no_inf_in_compute_polygon_vertex ( ) {
@@ -177,4 +159,15 @@ mod test
177
159
let nani32 = f64:: INFINITY as i32 ;
178
160
assert ! ( !buf. iter( ) . any( |& v| v. 0 == nani32 || v. 1 == nani32) ) ;
179
161
}
162
+
163
+ /// Correct 90 degree turn to the right
164
+ #[ test]
165
+ fn standard_corner ( ) {
166
+ let path = [ ( 10 , 10 ) , ( 20 , 10 ) , ( 20 , 20 ) ] ;
167
+ let mut buf = Vec :: new ( ) ;
168
+ compute_polygon_vertex ( & path, 2.0 , buf. as_mut ( ) ) ;
169
+ assert ! ( !buf. is_empty( ) ) ;
170
+ let buf2 = vec ! [ ( 18 , 12 ) ] ;
171
+ assert_eq ! ( buf, buf2) ;
172
+ }
180
173
}
0 commit comments