1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
// path.rs 2D vector paths.
//
// Copyright (c) 2017-2021 Douglas P Lau
//
use pointy::Pt;
/// Fill-rule for filling paths.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FillRule {
/// All points within bounds are filled
NonZero,
/// Alternate filling with path outline
EvenOdd,
}
/// Path operation.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PathOp {
/// Close the path
Close(),
/// Move to a point
Move(Pt<f32>),
/// Straight line to end point
Line(Pt<f32>),
/// Quadratic bézier curve (control point and end point)
Quad(Pt<f32>, Pt<f32>),
/// Cubic bézier curve (two control points and end point)
Cubic(Pt<f32>, Pt<f32>, Pt<f32>),
/// Set pen width (for stroking)
PenWidth(f32),
}
/// A `Path2D` is a builder for `Vec<PathOp>`.
///
/// # Example
/// ```
/// use footile::Path2D;
///
/// let path = Path2D::default()
/// .move_to(10.0, 10.0)
/// .line_to(90.0, 90.0)
/// .finish();
/// ```
pub struct Path2D {
/// Vec of path operations
ops: Vec<PathOp>,
/// Absolute vs relative coordinates
absolute: bool,
/// Current pen position
pen: Pt<f32>,
}
impl Default for Path2D {
fn default() -> Path2D {
let ops = Vec::with_capacity(32);
Path2D {
ops,
absolute: false,
pen: Pt::default(),
}
}
}
impl Path2D {
/// Use absolute coordinates for subsequent operations.
pub fn absolute(mut self) -> Self {
self.absolute = true;
self
}
/// Use relative coordinates for subsequent operations.
///
/// This is the default setting.
pub fn relative(mut self) -> Self {
self.absolute = false;
self
}
/// Get absolute point.
fn pt(&self, x: f32, y: f32) -> Pt<f32> {
if self.absolute {
Pt::new(x, y)
} else {
Pt::new(self.pen.x() + x, self.pen.y() + y)
}
}
/// Close current sub-path and move pen to origin.
pub fn close(mut self) -> Self {
self.ops.push(PathOp::Close());
self.pen = Pt::default();
self
}
/// Move the pen to a point.
///
/// * `x` X-position of point.
/// * `y` Y-position of point.
pub fn move_to(mut self, x: f32, y: f32) -> Self {
let pb = self.pt(x, y);
self.ops.push(PathOp::Move(pb));
self.pen = pb;
self
}
/// Add a line from pen to a point.
///
/// * `x` X-position of end point.
/// * `y` Y-position of end point.
pub fn line_to(mut self, x: f32, y: f32) -> Self {
let pb = self.pt(x, y);
self.ops.push(PathOp::Line(pb));
self.pen = pb;
self
}
/// Add a quadratic bézier spline.
///
/// The points are:
///
/// * Current pen position: P<sub>a</sub>
/// * Control point: P<sub>b</sub> (`bx` / `by`)
/// * Spline end point: P<sub>c</sub> (`cx` / `cy`)
pub fn quad_to(mut self, bx: f32, by: f32, cx: f32, cy: f32) -> Self {
let pb = self.pt(bx, by);
let pc = self.pt(cx, cy);
self.ops.push(PathOp::Quad(pb, pc));
self.pen = pc;
self
}
/// Add a cubic bézier spline.
///
/// The points are:
///
/// * Current pen position: P<sub>a</sub>
/// * First control point: P<sub>b</sub> (`bx` / `by`)
/// * Second control point: P<sub>c</sub> (`cx` / `cy`)
/// * Spline end point: P<sub>d</sub> (`dx` / `dy`)
pub fn cubic_to(
mut self,
bx: f32,
by: f32,
cx: f32,
cy: f32,
dx: f32,
dy: f32,
) -> Self {
let pb = self.pt(bx, by);
let pc = self.pt(cx, cy);
let pd = self.pt(dx, dy);
self.ops.push(PathOp::Cubic(pb, pc, pd));
self.pen = pd;
self
}
/// Set pen stroke width.
///
/// All subsequent path points will be affected, until the stroke width
/// is changed again.
///
/// * `width` Pen stroke width.
pub fn pen_width(mut self, width: f32) -> Self {
self.ops.push(PathOp::PenWidth(width));
self
}
/// Finish path with specified operations.
pub fn finish(self) -> Vec<PathOp> {
self.ops
}
}