use path::*;
use math::*;
use math_utils::fuzzy_eq;
use bezier::*;
use arc::arc_to_cubic_beziers;
use super::{
vertex_id_range,
};
pub type BezierPathBuilder = SvgPathBuilder<PrimitiveImpl>;
pub type FlattenedPathBuilder = SvgPathBuilder<FlattenedBuilder<PrimitiveImpl>>;
pub fn flattened_path_builder(tolerance: f32) -> FlattenedPathBuilder {
SvgPathBuilder::from_builder(FlattenedBuilder::new(PrimitiveImpl::new(), tolerance))
}
pub fn bezier_path_builder() -> BezierPathBuilder {
SvgPathBuilder::from_builder(PrimitiveImpl::new())
}
pub trait PrimitiveBuilder {
type PathType;
fn move_to(&mut self, to: Point);
fn line_to(&mut self, to: Point);
fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point);
fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point);
fn end(&mut self) -> PathId;
fn close(&mut self) -> PathId;
fn current_position(&self) -> Point;
fn build(self) -> Self::PathType;
}
pub trait SvgBuilder : PrimitiveBuilder {
fn relative_move_to(&mut self, to: Vec2);
fn relative_line_to(&mut self, to: Vec2);
fn relative_quadratic_bezier_to(&mut self, ctrl: Vec2, to: Vec2);
fn relative_cubic_bezier_to(&mut self, ctrl1: Vec2, ctrl2: Vec2, to: Vec2);
fn cubic_bezier_smooth_to(&mut self, ctrl2: Point, to: Point);
fn relative_cubic_bezier_smooth_to(&mut self, ctrl2: Vec2, to: Vec2);
fn quadratic_bezier_smooth_to(&mut self, to: Point);
fn relative_quadratic_bezier_smooth_to(&mut self, to: Vec2);
fn horizontal_line_to(&mut self, x: f32);
fn relative_horizontal_line_to(&mut self, dx: f32);
fn vertical_line_to(&mut self, y: f32);
fn relative_vertical_line_to(&mut self, dy: f32);
fn arc_to(&mut self, to: Point, radii: Vec2, x_rotation: f32, flags: ArcFlags);
fn relative_arc_to(&mut self, to: Vec2, radii: Vec2, x_rotation: f32, flags: ArcFlags);
}
pub trait PolygonBuilder {
fn polygon(&mut self, points: &[Point]) -> PathId;
}
#[derive(Copy, Clone, Debug)]
pub struct ArcFlags {
pub large_arc: bool,
pub sweep: bool,
}
pub struct SvgPathBuilder<Builder: PrimitiveBuilder> {
builder: Builder,
last_ctrl: Point,
}
impl<Builder: PrimitiveBuilder> SvgPathBuilder<Builder> {
pub fn from_builder(builder: Builder) -> SvgPathBuilder<Builder> {
SvgPathBuilder {
builder: builder,
last_ctrl: vec2(0.0, 0.0),
}
}
}
impl<Builder: PrimitiveBuilder> PrimitiveBuilder for SvgPathBuilder<Builder> {
type PathType = Builder::PathType;
fn move_to(&mut self, to: Point) {
self.last_ctrl = to;
self.builder.move_to(to);
}
fn line_to(&mut self, to: Point) {
self.last_ctrl = self.current_position();
self.builder.line_to(to);
}
fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
self.last_ctrl = ctrl;
self.builder.quadratic_bezier_to(ctrl, to);
}
fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
self.last_ctrl = ctrl2;
self.builder.cubic_bezier_to(ctrl1, ctrl2, to);
}
fn end(&mut self) -> PathId {
self.last_ctrl = point(0.0, 0.0);
self.builder.end()
}
fn close(&mut self) -> PathId {
self.last_ctrl = point(0.0, 0.0);
self.builder.close()
}
fn current_position(&self) -> Vec2 {
self.builder.current_position()
}
fn build(self) -> Builder::PathType { self.builder.build() }
}
impl<Builder: PrimitiveBuilder> SvgBuilder for SvgPathBuilder<Builder> {
fn relative_move_to(&mut self, to: Vec2) {
let offset = self.builder.current_position();
self.move_to(offset + to);
}
fn relative_line_to(&mut self, to: Vec2) {
let offset = self.builder.current_position();
self.line_to(offset + to);
}
fn relative_quadratic_bezier_to(&mut self, ctrl: Vec2, to: Vec2) {
let offset = self.builder.current_position();
self.quadratic_bezier_to(ctrl + offset, to + offset);
}
fn relative_cubic_bezier_to(&mut self, ctrl1: Vec2, ctrl2: Vec2, to: Vec2) {
let offset = self.builder.current_position();
self.cubic_bezier_to(ctrl1 + offset, ctrl2 + offset, to + offset);
}
fn cubic_bezier_smooth_to(&mut self, ctrl2: Point, to: Point) {
let ctrl = self.builder.current_position() + (self.builder.current_position() - self.last_ctrl);
self.cubic_bezier_to(ctrl, ctrl2, to);
}
fn relative_cubic_bezier_smooth_to(&mut self, ctrl2: Vec2, to: Vec2) {
let ctrl = self.builder.current_position() - self.last_ctrl;
self.relative_cubic_bezier_to(ctrl, ctrl2, to);
}
fn quadratic_bezier_smooth_to(&mut self, to: Point) {
let ctrl = self.builder.current_position() + (self.builder.current_position() - self.last_ctrl);
self.quadratic_bezier_to(ctrl, to);
}
fn relative_quadratic_bezier_smooth_to(&mut self, to: Vec2) {
let ctrl = self.builder.current_position() - self.last_ctrl;
self.relative_quadratic_bezier_to(ctrl, to);
}
fn horizontal_line_to(&mut self, x: f32) {
let y = self.builder.current_position().y;
self.line_to(vec2(x, y));
}
fn relative_horizontal_line_to(&mut self, dx: f32) {
let p = self.builder.current_position();
self.line_to(vec2(p.x + dx, p.y));
}
fn vertical_line_to(&mut self, y: f32) {
let x = self.builder.current_position().x;
self.line_to(vec2(x, y));
}
fn relative_vertical_line_to(&mut self, dy: f32) {
let p = self.builder.current_position();
self.line_to(vec2(p.x, p.y + dy));
}
fn arc_to(&mut self, to: Point, radii: Vec2, x_rotation: f32, flags: ArcFlags) {
if self.current_position() == to {
return;
}
arc_to_cubic_beziers(
self.current_position(),
to, radii, x_rotation, flags,
self
);
}
fn relative_arc_to(&mut self, to: Vec2, radii: Vec2, x_rotation: f32, flags: ArcFlags) {
let offset = self.builder.current_position();
self.arc_to(to + offset, radii, x_rotation, flags);
}
}
pub struct FlattenedBuilder<Builder> {
builder: Builder,
tolerance: f32,
}
pub struct PrimitiveImpl {
vertices: Vec<PointData>,
path_info: Vec<PathInfo>,
last_position: Vec2,
top_left: Vec2,
bottom_right: Vec2,
offset: u16,
building: bool,
}
impl<Builder: PrimitiveBuilder> PrimitiveBuilder for FlattenedBuilder<Builder> {
type PathType = Builder::PathType;
fn move_to(&mut self, to: Point) { self.builder.move_to(to); }
fn line_to(&mut self, to: Point) { self.builder.line_to(to); }
fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
let from = self.current_position();
let cubic = QuadraticBezierSegment { from: from, cp: ctrl, to: to }.to_cubic();
flatten_cubic_bezier(cubic, self.tolerance, self);
}
fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
flatten_cubic_bezier(
CubicBezierSegment{
from: self.current_position(),
cp1: ctrl1,
cp2: ctrl2,
to: to,
},
self.tolerance,
self
);
}
fn end(&mut self) -> PathId { self.builder.end() }
fn close(&mut self) -> PathId { self.builder.close() }
fn current_position(&self) -> Point { self.builder.current_position() }
fn build(self) -> Builder::PathType { self.builder.build() }
}
impl PrimitiveBuilder for PrimitiveImpl {
type PathType = Path;
fn move_to(&mut self, to: Point)
{
if self.building {
self.end_sub_path(false);
}
self.top_left = to;
self.bottom_right = to;
self.push(to, PointType::Normal);
}
fn line_to(&mut self, to: Point) {
self.push(to, PointType::Normal);
}
fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
self.push(ctrl, PointType::Control);
self.push(to, PointType::Normal);
}
fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
self.push(ctrl1, PointType::Control);
self.push(ctrl2, PointType::Control);
self.push(to, PointType::Normal);
}
fn end(&mut self) -> PathId { self.end_sub_path(false) }
fn close(&mut self) -> PathId { self.end_sub_path(true) }
fn current_position(&self) -> Point { self.last_position }
fn build(mut self) -> Path {
if self.building {
self.end();
}
return Path::from_vec(self.vertices, self.path_info);
}
}
impl<Builder: PrimitiveBuilder> FlattenedBuilder<Builder> {
pub fn new(builder: Builder, tolerance: f32) -> FlattenedBuilder<Builder> {
FlattenedBuilder {
builder: builder,
tolerance: tolerance,
}
}
pub fn set_tolerance(&mut self, tolerance: f32) { self.tolerance = tolerance }
}
impl PrimitiveImpl {
pub fn new() -> PrimitiveImpl {
PrimitiveImpl {
vertices: Vec::with_capacity(512),
path_info: Vec::with_capacity(16),
last_position: vec2(0.0, 0.0),
top_left: vec2(0.0, 0.0),
bottom_right: vec2(0.0, 0.0),
offset: 0,
building: false,
}
}
pub fn begin_sub_path(&mut self) {
self.offset = self.vertices.len() as u16;
self.building = true;
}
pub fn end_sub_path(&mut self, mut closed: bool) -> PathId {
self.building = false;
let vertices_len = self.vertices.len();
if vertices_len == 0 {
return path_id(self.path_info.len() as u16);
}
let offset = self.offset as usize;
let last = vertices_len - 1;
let last = if last > 0 && fuzzy_eq(self.vertices[last].position, self.vertices[offset].position) {
self.vertices.pop();
closed = true;
last - 1
} else { last };
let vertex_count = last + 1 - offset;
if vertex_count == 0 {
return path_id(self.path_info.len() as u16);
}
let vertex_range = vertex_id_range(self.offset, self.offset + vertex_count as u16);
let aabb = Rect::new(self.top_left,
size(self.bottom_right.x - self.top_left.x, self.bottom_right.y - self.top_left.y),
);
let shape_info = PathInfo {
range: vertex_range,
aabb: aabb,
has_beziers: Some(false),
is_closed: closed,
};
let index = path_id(self.path_info.len() as u16);
self.path_info.push(shape_info);
return index;
}
pub fn push(&mut self, point: Vec2, ptype: PointType) {
debug_assert!(!point.x.is_nan());
debug_assert!(!point.y.is_nan());
if self.building && point == self.last_position {
return;
}
if !self.building {
self.begin_sub_path();
}
if self.vertices.len() == 0 {
self.top_left = point;
self.bottom_right = point;
} else {
if point.x < self.top_left.x { self.top_left.x = point.x; }
if point.y < self.top_left.y { self.top_left.y = point.y; }
if point.x > self.bottom_right.x { self.bottom_right.x = point.x; }
if point.y > self.bottom_right.y { self.bottom_right.y = point.y; }
}
self.vertices.push(PointData{ position: point, point_type: ptype });
self.last_position = point;
}
}
impl<Builder: PrimitiveBuilder> PolygonBuilder for Builder {
fn polygon(&mut self, points: &[Point]) -> PathId {
assert!(!points.is_empty());
self.move_to(points[0]);
for p in points[1..].iter() {
self.line_to(*p);
}
return self.close();
}
}
#[test]
fn test_path_builder_empty_path() {
let _ = flattened_path_builder(0.05).build();
}
#[test]
fn test_path_builder_empty_sub_path() {
let mut builder = flattened_path_builder(0.05);
builder.move_to(vec2(0.0, 0.0));
builder.move_to(vec2(1.0, 0.0));
builder.move_to(vec2(2.0, 0.0));
let _ = builder.build();
}
#[test]
fn test_path_builder_close_empty() {
let mut builder = flattened_path_builder(0.05);
builder.end();
builder.close();
builder.end();
builder.end();
builder.close();
builder.close();
let _ = builder.build();
}
#[test]
fn test_path_builder_simple() {
{
let mut path = flattened_path_builder(0.05);
path.move_to(vec2(0.0, 0.0));
path.line_to(vec2(1.0, 0.0));
path.line_to(vec2(1.0, 1.0));
let id = path.close();
let path = path.build();
let info = path.sub_path(id).info();
assert_eq!(path.vertices().nth(0).position, vec2(0.0, 0.0));
assert_eq!(path.vertices().nth(1).position, vec2(1.0, 0.0));
assert_eq!(path.vertices().nth(2).position, vec2(1.0, 1.0));
assert_eq!(path.vertices().nth(0).point_type, PointType::Normal);
assert_eq!(path.vertices().nth(1).point_type, PointType::Normal);
assert_eq!(path.vertices().nth(2).point_type, PointType::Normal);
assert_eq!(info.range, vertex_id_range(0, 3));
assert_eq!(info.aabb, Rect::new(vec2(0.0, 0.0), size(1.0, 1.0)));
let sub_path = path.sub_path(id);
let first = sub_path.first();
let next = sub_path.next(first);
let prev = sub_path.previous(first);
assert!(first != next);
assert!(first != prev);
assert!(next != prev);
assert_eq!(first, sub_path.previous(next));
assert_eq!(first, sub_path.next(prev));
}
{
let mut path = flattened_path_builder(0.05);
path.move_to(vec2(0.0, 0.0));
path.line_to(vec2(1.0, 1.0));
path.line_to(vec2(1.0, 0.0));
let id = path.close();
let path = path.build();
let info = path.sub_path(id).info();
assert_eq!(info.range, vertex_id_range(0, 3));
assert_eq!(info.aabb, Rect::new(vec2(0.0, 0.0), size(1.0, 1.0)));
}
{
let mut path = flattened_path_builder(0.05);
path.move_to(vec2(0.0, 0.0));
path.line_to(vec2(1.0, 1.0));
path.line_to(vec2(1.0, 0.0));
path.line_to(vec2(0.0, 0.0));
let id = path.close();
let path = path.build();
let info = path.sub_path(id).info();
assert_eq!(info.range, vertex_id_range(0, 3));
assert_eq!(info.aabb, Rect::new(vec2(0.0, 0.0), size(1.0, 1.0)));
}
}
#[test]
fn test_path_builder_simple_bezier() {
{
let mut path = bezier_path_builder();
path.move_to(vec2(0.0, 0.0));
path.quadratic_bezier_to(vec2(1.0, 0.0), vec2(1.0, 1.0));
let id = path.close();
let path = path.build();
let info = path.sub_path(id).info();
assert_eq!(info.range, vertex_id_range(0, 3));
assert_eq!(info.aabb, Rect::new(vec2(0.0, 0.0), size(1.0, 1.0)));
}
{
let mut path = bezier_path_builder();
path.move_to(vec2(0.0, 0.0));
path.quadratic_bezier_to(vec2(1.0, 1.0), vec2(1.0, 0.0));
let id = path.close();
let path = path.build();
let info = path.sub_path(id).info();
assert_eq!(info.range, vertex_id_range(0, 3));
assert_eq!(info.aabb, Rect::new(vec2(0.0, 0.0), size(1.0, 1.0)));
}
{
let mut path = bezier_path_builder();
path.move_to(vec2(0.0, 0.0));
path.line_to(vec2(0.1, 0.0));
path.line_to(vec2(0.2, 0.1));
path.line_to(vec2(0.3, 0.1));
path.line_to(vec2(0.4, 0.0));
path.line_to(vec2(0.5, 0.0));
path.quadratic_bezier_to(vec2(0.5, 0.4), vec2(0.3, 0.4));
path.line_to(vec2(0.1, 0.4));
path.quadratic_bezier_to(vec2(-0.2, 0.1), vec2(-0.1, 0.0));
let id = path.close();
let path = path.build();
let info = path.sub_path(id).info();
assert_eq!(info.aabb, Rect::new(vec2(-0.2, 0.0), size(0.7, 0.4)));
}
}
#[test]
fn test_arc_simple() {
let mut path = bezier_path_builder();
path.move_to(vec2(180.0, 180.0));
path.arc_to(
vec2(160.0, 220.0), vec2(20.0, 40.0) , 0.0,
ArcFlags { large_arc: true, sweep: false }
);
path.move_to(vec2(180.0, 180.0));
path.arc_to(
vec2(160.0, 220.0), vec2(20.0, 40.0) , 0.0,
ArcFlags { large_arc: true, sweep: true }
);
path.move_to(vec2(260.0, 150.0));
path.arc_to(
vec2(240.0, 190.0), vec2(20.0, 40.0) , 0.0,
ArcFlags {large_arc: false, sweep: true}
);
path.build();
}