1use crate::outline::{ArcDirection, Contour, ContourIterFlags, Outline, PushSegmentFlags};
14use crate::segment::Segment;
15use pathfinder_geometry::line_segment::LineSegment2F;
16use pathfinder_geometry::rect::RectF;
17use pathfinder_geometry::transform2d::Transform2F;
18use pathfinder_geometry::util::EPSILON;
19use pathfinder_geometry::vector::{Vector2F, vec2f};
20use std::f32;
21
22const TOLERANCE: f32 = 0.01;
23
24pub struct OutlineStrokeToFill<'a> {
25 input: &'a Outline,
26 output: Outline,
27 style: StrokeStyle,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct StrokeStyle {
32 pub line_width: f32,
33 pub line_cap: LineCap,
34 pub line_join: LineJoin,
35}
36
37#[derive(Clone, Copy, Debug, PartialEq)]
38pub enum LineCap {
39 Butt,
40 Square,
41 Round,
42}
43
44#[derive(Clone, Copy, Debug, PartialEq)]
45pub enum LineJoin {
46 Miter(f32),
47 Bevel,
48 Round,
49}
50
51impl<'a> OutlineStrokeToFill<'a> {
52 #[inline]
53 pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill {
54 OutlineStrokeToFill { input, output: Outline::new(), style }
55 }
56
57 pub fn offset(&mut self) {
58 let mut new_contours = vec![];
59 for input in &self.input.contours {
60 let closed = input.closed;
61 let mut stroker = ContourStrokeToFill::new(input,
62 Contour::new(),
63 self.style.line_width * 0.5,
64 self.style.line_join);
65
66 stroker.offset_forward();
67 if closed {
68 self.push_stroked_contour(&mut new_contours, stroker, true);
69 stroker = ContourStrokeToFill::new(input,
70 Contour::new(),
71 self.style.line_width * 0.5,
72 self.style.line_join);
73 } else {
74 self.add_cap(&mut stroker.output);
75 }
76
77 stroker.offset_backward();
78 if !closed {
79 self.add_cap(&mut stroker.output);
80 }
81
82 self.push_stroked_contour(&mut new_contours, stroker, closed);
83 }
84
85 let mut new_bounds = None;
86 new_contours.iter().for_each(|contour| contour.update_bounds(&mut new_bounds));
87
88 self.output.contours = new_contours;
89 self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default());
90 }
91
92 #[inline]
93 pub fn into_outline(self) -> Outline {
94 self.output
95 }
96
97 fn push_stroked_contour(&mut self,
98 new_contours: &mut Vec<Contour>,
99 mut stroker: ContourStrokeToFill,
100 closed: bool) {
101 if closed && stroker.output.might_need_join(self.style.line_join) {
103 let (p1, p0) = (stroker.output.position_of(1), stroker.output.position_of(0));
104 let final_segment = LineSegment2F::new(p1, p0);
105 stroker.output.add_join(self.style.line_width * 0.5,
106 self.style.line_join,
107 stroker.input.position_of(0),
108 final_segment);
109 }
110
111 stroker.output.closed = true;
112 new_contours.push(stroker.output);
113 }
114
115 fn add_cap(&mut self, contour: &mut Contour) {
116 if self.style.line_cap == LineCap::Butt || contour.len() < 2 {
117 return
118 }
119
120 let width = self.style.line_width;
121 let p1 = contour.position_of_last(1);
122
123 let mut p0;
125 let mut p0_index = contour.len() - 2;
126 loop {
127 p0 = contour.position_of(p0_index);
128 if (p1 - p0).square_length() > EPSILON {
129 break;
130 }
131 if p0_index == 0 {
132 return;
133 }
134 p0_index -= 1;
135 }
136 let gradient = (p1 - p0).normalize();
137
138 match self.style.line_cap {
139 LineCap::Butt => unreachable!(),
140
141 LineCap::Square => {
142 let offset = gradient * (width * 0.5);
143
144 let p2 = p1 + offset;
145 let p3 = p2 + gradient.yx() * vec2f(-width, width);
146 let p4 = p3 - offset;
147
148 contour.push_endpoint(p2);
149 contour.push_endpoint(p3);
150 contour.push_endpoint(p4);
151 }
152
153 LineCap::Round => {
154 let scale = width * 0.5;
155 let offset = gradient.yx() * vec2f(-1.0, 1.0);
156 let translation = p1 + offset * (width * 0.5);
157 let transform = Transform2F::from_scale(scale).translate(translation);
158 let chord = LineSegment2F::new(-offset, offset);
159 contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
160 }
161 }
162 }
163}
164
165struct ContourStrokeToFill<'a> {
166 input: &'a Contour,
167 output: Contour,
168 radius: f32,
169 join: LineJoin,
170}
171
172impl<'a> ContourStrokeToFill<'a> {
173 #[inline]
174 fn new(input: &Contour, output: Contour, radius: f32, join: LineJoin) -> ContourStrokeToFill {
175 ContourStrokeToFill { input, output, radius, join }
176 }
177
178 fn offset_forward(&mut self) {
179 for (segment_index, segment) in self.input.iter(ContourIterFlags::empty()).enumerate() {
180 let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
183 segment.offset(-self.radius, join, &mut self.output);
184 }
185 }
186
187 fn offset_backward(&mut self) {
188 let mut segments: Vec<_> = self
189 .input
190 .iter(ContourIterFlags::empty())
191 .map(|segment| segment.reversed())
192 .collect();
193 segments.reverse();
194 for (segment_index, segment) in segments.iter().enumerate() {
195 let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
198 segment.offset(-self.radius, join, &mut self.output);
199 }
200 }
201}
202
203trait Offset {
204 fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour);
205 fn add_to_contour(&self,
206 distance: f32,
207 join: LineJoin,
208 join_point: Vector2F,
209 contour: &mut Contour);
210 fn offset_once(&self, distance: f32) -> Self;
211 fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool;
212}
213
214impl Offset for Segment {
215 fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour) {
216 let join_point = self.baseline.from();
217 if self.baseline.square_length() < TOLERANCE * TOLERANCE {
218 self.add_to_contour(distance, join, join_point, contour);
219 return;
220 }
221
222 let candidate = self.offset_once(distance);
223 if self.error_is_within_tolerance(&candidate, distance) {
224 candidate.add_to_contour(distance, join, join_point, contour);
225 return;
226 }
227
228 debug!("--- SPLITTING ---");
229 debug!("... PRE-SPLIT: {:?}", self);
230 let (before, after) = self.split(0.5);
231 debug!("... AFTER-SPLIT: {:?} {:?}", before, after);
232 before.offset(distance, join, contour);
233 after.offset(distance, join, contour);
234 }
235
236 fn add_to_contour(&self,
237 distance: f32,
238 join: LineJoin,
239 join_point: Vector2F,
240 contour: &mut Contour) {
241 if contour.might_need_join(join) {
243 let p3 = self.baseline.from();
244 let p4 = if self.is_line() {
245 self.baseline.to()
246 } else {
247 self.ctrl.from()
250 };
251
252 contour.add_join(distance, join, join_point, LineSegment2F::new(p4, p3));
253 }
254
255 let flags = PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT;
257 contour.push_segment(self, flags);
258 }
259
260 fn offset_once(&self, distance: f32) -> Segment {
261 if self.is_line() {
262 return Segment::line(self.baseline.offset(distance));
263 }
264
265 if self.is_quadratic() {
266 let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.from());
267 let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to());
268 segment_0 = segment_0.offset(distance);
269 segment_1 = segment_1.offset(distance);
270 let ctrl = match segment_0.intersection_t(segment_1) {
271 Some(t) => segment_0.sample(t),
272 None => segment_0.to().lerp(segment_1.from(), 0.5),
273 };
274 let baseline = LineSegment2F::new(segment_0.from(), segment_1.to());
275 return Segment::quadratic(baseline, ctrl);
276 }
277
278 debug_assert!(self.is_cubic());
279
280 if self.baseline.from() == self.ctrl.from() {
281 let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.to());
282 let mut segment_1 = LineSegment2F::new(self.ctrl.to(), self.baseline.to());
283 segment_0 = segment_0.offset(distance);
284 segment_1 = segment_1.offset(distance);
285 let ctrl = match segment_0.intersection_t(segment_1) {
286 Some(t) => segment_0.sample(t),
287 None => segment_0.to().lerp(segment_1.from(), 0.5),
288 };
289 let baseline = LineSegment2F::new(segment_0.from(), segment_1.to());
290 let ctrl = LineSegment2F::new(segment_0.from(), ctrl);
291 return Segment::cubic(baseline, ctrl);
292 }
293
294 if self.ctrl.to() == self.baseline.to() {
295 let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.from());
296 let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to());
297 segment_0 = segment_0.offset(distance);
298 segment_1 = segment_1.offset(distance);
299 let ctrl = match segment_0.intersection_t(segment_1) {
300 Some(t) => segment_0.sample(t),
301 None => segment_0.to().lerp(segment_1.from(), 0.5),
302 };
303 let baseline = LineSegment2F::new(segment_0.from(), segment_1.to());
304 let ctrl = LineSegment2F::new(ctrl, segment_1.to());
305 return Segment::cubic(baseline, ctrl);
306 }
307
308 let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.from());
309 let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.ctrl.to());
310 let mut segment_2 = LineSegment2F::new(self.ctrl.to(), self.baseline.to());
311 segment_0 = segment_0.offset(distance);
312 segment_1 = segment_1.offset(distance);
313 segment_2 = segment_2.offset(distance);
314 let (ctrl_0, ctrl_1) = match (
315 segment_0.intersection_t(segment_1),
316 segment_1.intersection_t(segment_2),
317 ) {
318 (Some(t0), Some(t1)) => (segment_0.sample(t0), segment_1.sample(t1)),
319 _ => (
320 segment_0.to().lerp(segment_1.from(), 0.5),
321 segment_1.to().lerp(segment_2.from(), 0.5),
322 ),
323 };
324 let baseline = LineSegment2F::new(segment_0.from(), segment_2.to());
325 let ctrl = LineSegment2F::new(ctrl_0, ctrl_1);
326 Segment::cubic(baseline, ctrl)
327 }
328
329 fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool {
330 let (mut min, mut max) = (
331 f32::abs(distance) - TOLERANCE,
332 f32::abs(distance) + TOLERANCE,
333 );
334 min = if min <= 0.0 { 0.0 } else { min * min };
335 max = if max <= 0.0 { 0.0 } else { max * max };
336
337 for t_num in 0..(SAMPLE_COUNT + 1) {
338 let t = t_num as f32 / SAMPLE_COUNT as f32;
339 let (this_p, other_p) = (self.sample(t), other.sample(t));
341 let vector = this_p - other_p;
342 let square_distance = vector.square_length();
343 debug!(
344 "this_p={:?} other_p={:?} vector={:?} sqdist={:?} min={:?} max={:?}",
345 this_p, other_p, vector, square_distance, min, max
346 );
347 if square_distance < min || square_distance > max {
348 return false;
349 }
350 }
351
352 return true;
353
354 const SAMPLE_COUNT: u32 = 16;
355 }
356}
357
358impl Contour {
359 fn might_need_join(&self, join: LineJoin) -> bool {
360 if self.len() < 2 {
361 false
362 } else {
363 match join {
364 LineJoin::Miter(_) | LineJoin::Round => true,
365 LineJoin::Bevel => false,
366 }
367 }
368 }
369
370 fn add_join(&mut self,
371 distance: f32,
372 join: LineJoin,
373 join_point: Vector2F,
374 next_tangent: LineSegment2F) {
375 let (p0, p1) = (self.position_of_last(2), self.position_of_last(1));
376 let prev_tangent = LineSegment2F::new(p0, p1);
377
378 if prev_tangent.square_length() < EPSILON || next_tangent.square_length() < EPSILON {
379 return;
380 }
381
382 match join {
383 LineJoin::Bevel => {}
384 LineJoin::Miter(miter_limit) => {
385 if let Some(prev_tangent_t) = prev_tangent.intersection_t(next_tangent) {
386 if prev_tangent_t < -EPSILON {
387 return;
388 }
389 let miter_endpoint = prev_tangent.sample(prev_tangent_t);
390 let threshold = miter_limit * distance;
391 if (miter_endpoint - join_point).square_length() > threshold * threshold {
392 return;
393 }
394 self.push_endpoint(miter_endpoint);
395 }
396 }
397 LineJoin::Round => {
398 let scale = distance.abs();
399 let transform = Transform2F::from_scale(scale).translate(join_point);
400 let chord_from = (prev_tangent.to() - join_point).normalize();
401 let chord_to = (next_tangent.to() - join_point).normalize();
402 let chord = LineSegment2F::new(chord_from, chord_to);
403 self.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
404 }
405 }
406 }
407}
408
409impl Default for StrokeStyle {
410 #[inline]
411 fn default() -> StrokeStyle {
412 StrokeStyle {
413 line_width: 1.0,
414 line_cap: LineCap::default(),
415 line_join: LineJoin::default(),
416 }
417 }
418}
419
420impl Default for LineCap {
421 #[inline]
422 fn default() -> LineCap { LineCap::Butt }
423}
424
425impl Default for LineJoin {
426 #[inline]
427 fn default() -> LineJoin { LineJoin::Miter(10.0) }
428}