Implement round line caps

This commit is contained in:
Patrick Walton 2019-05-21 20:21:46 -07:00
parent 197f01d56a
commit 2131724a2a
2 changed files with 44 additions and 15 deletions

View File

@ -19,10 +19,12 @@ use crate::clip::{self, ContourPolygonClipper, ContourRectClipper};
use crate::dilation::ContourDilator;
use crate::orientation::Orientation;
use crate::segment::{Segment, SegmentFlags, SegmentKind};
use std::f32::consts::FRAC_PI_2;
use std::f32::consts::{FRAC_PI_2, PI};
use std::fmt::{self, Debug, Formatter};
use std::mem;
const TWO_PI: f32 = PI * 2.0;
#[derive(Clone)]
pub struct Outline {
pub(crate) contours: Vec<Contour>,
@ -367,7 +369,17 @@ impl Contour {
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
}
pub fn push_arc(&mut self, center: Point2DF32, radius: f32, start_angle: f32, end_angle: f32) {
pub fn push_arc(&mut self,
center: Point2DF32,
radius: f32,
mut start_angle: f32,
mut end_angle: f32) {
start_angle %= TWO_PI;
end_angle %= TWO_PI;
if end_angle < start_angle {
end_angle += TWO_PI;
}
let scale = Transform2DF32::from_scale(Point2DF32::splat(radius));
let translation = Transform2DF32::from_translation(center);
@ -375,7 +387,7 @@ impl Contour {
while angle < end_angle {
let sweep_angle = f32::min(FRAC_PI_2, end_angle - angle);
let mut segment = Segment::arc(sweep_angle);
let rotation = Transform2DF32::from_rotation(angle);
let rotation = Transform2DF32::from_rotation(sweep_angle * 0.5 + angle);
segment = segment.transform(&scale.post_mul(&rotation).post_mul(&translation));
debug!("angle={} start_angle={} end_angle={} sweep_angle={} segment={:?}",

View File

@ -15,6 +15,8 @@ use crate::basic::point::Point2DF32;
use crate::basic::rect::RectF32;
use crate::outline::{Contour, Outline};
use crate::segment::Segment;
use std::f32::consts::FRAC_PI_2;
use std::f32;
use std::mem;
const TOLERANCE: f32 = 0.01;
@ -35,6 +37,7 @@ pub struct StrokeStyle {
pub enum LineCap {
Butt,
Square,
Round,
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -70,9 +73,7 @@ impl OutlineStrokeToFill {
}
stroker.offset_backward();
if closed {
// TODO(pcwalton): Line join.
} else {
if !closed {
self.add_cap(&mut stroker.output);
}
@ -108,16 +109,28 @@ impl OutlineStrokeToFill {
let width = self.style.line_width;
let (p0, p1) = (contour.position_of_last(2), contour.position_of_last(1));
let gradient = (p1 - p0).normalize();
match self.style.line_cap {
LineCap::Butt => unreachable!(),
LineCap::Square => {
let offset = gradient.scale(width * 0.5);
let p2 = p1 + offset;
let p3 = p2 + gradient.yx().scale_xy(Point2DF32::new(width, -width));
let p3 = p2 + gradient.yx().scale_xy(Point2DF32::new(-width, width));
let p4 = p3 - offset;
contour.push_endpoint(p2);
contour.push_endpoint(p3);
contour.push_endpoint(p4);
}
LineCap::Round => {
// FIXME(pcwalton): Should we really be using angles here at all?
let offset = gradient.yx().scale_xy(Point2DF32::new(-width * 0.5, width * 0.5));
let angle = f32::atan2(gradient.y(), gradient.x());
contour.push_arc(p1 + offset, width * 0.5, angle - FRAC_PI_2, angle + FRAC_PI_2);
}
}
}
}
struct ContourStrokeToFill {
@ -135,8 +148,10 @@ impl ContourStrokeToFill {
fn offset_forward(&mut self) {
for (segment_index, segment) in self.input.iter().enumerate() {
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
// clockwise. Of course, we should just implement anticlockwise arcs to begin with...
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
segment.offset(self.radius, join, &mut self.output);
segment.offset(-self.radius, join, &mut self.output);
}
}
@ -148,8 +163,10 @@ impl ContourStrokeToFill {
.collect();
segments.reverse();
for (segment_index, segment) in segments.iter().enumerate() {
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
// clockwise. Of course, we should just implement anticlockwise arcs to begin with...
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
segment.offset(self.radius, join, &mut self.output);
segment.offset(-self.radius, join, &mut self.output);
}
}
}