2021-03-29 23:28:35 +00:00
|
|
|
use super::scene::{Path, PathVertex};
|
2021-08-03 19:48:58 +00:00
|
|
|
use crate::{color::Color, json::ToJson};
|
2021-03-30 04:46:26 +00:00
|
|
|
pub use pathfinder_geometry::*;
|
|
|
|
use rect::RectF;
|
2023-08-18 07:03:46 +00:00
|
|
|
use refineable::Refineable;
|
2021-08-03 19:48:58 +00:00
|
|
|
use serde::{Deserialize, Deserializer};
|
2021-04-07 05:50:13 +00:00
|
|
|
use serde_json::json;
|
2021-03-29 17:50:38 +00:00
|
|
|
use vector::{vec2f, Vector2F};
|
|
|
|
|
2021-03-29 23:28:35 +00:00
|
|
|
pub struct PathBuilder {
|
|
|
|
vertices: Vec<PathVertex>,
|
2021-03-29 17:50:38 +00:00
|
|
|
start: Vector2F,
|
|
|
|
current: Vector2F,
|
2021-03-29 23:28:35 +00:00
|
|
|
contour_count: usize,
|
2021-03-30 04:46:26 +00:00
|
|
|
bounds: RectF,
|
2021-03-29 17:50:38 +00:00
|
|
|
}
|
|
|
|
|
2021-03-29 23:28:35 +00:00
|
|
|
enum PathVertexKind {
|
2021-03-29 17:50:38 +00:00
|
|
|
Solid,
|
|
|
|
Quadratic,
|
|
|
|
}
|
|
|
|
|
2022-08-10 21:39:24 +00:00
|
|
|
impl Default for PathBuilder {
|
|
|
|
fn default() -> Self {
|
|
|
|
PathBuilder::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 23:28:35 +00:00
|
|
|
impl PathBuilder {
|
|
|
|
pub fn new() -> Self {
|
2021-03-29 17:50:38 +00:00
|
|
|
Self {
|
|
|
|
vertices: Vec::new(),
|
|
|
|
start: vec2f(0., 0.),
|
|
|
|
current: vec2f(0., 0.),
|
2021-03-29 23:28:35 +00:00
|
|
|
contour_count: 0,
|
2021-03-30 04:46:26 +00:00
|
|
|
bounds: RectF::default(),
|
2021-03-29 17:50:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reset(&mut self, point: Vector2F) {
|
|
|
|
self.vertices.clear();
|
|
|
|
self.start = point;
|
|
|
|
self.current = point;
|
2021-03-29 23:28:35 +00:00
|
|
|
self.contour_count = 0;
|
2021-03-29 17:50:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn line_to(&mut self, point: Vector2F) {
|
2021-03-29 23:28:35 +00:00
|
|
|
self.contour_count += 1;
|
|
|
|
if self.contour_count > 1 {
|
|
|
|
self.push_triangle(self.start, self.current, point, PathVertexKind::Solid);
|
2021-03-29 17:50:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.current = point;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn curve_to(&mut self, point: Vector2F, ctrl: Vector2F) {
|
2021-03-29 23:28:35 +00:00
|
|
|
self.contour_count += 1;
|
|
|
|
if self.contour_count > 1 {
|
|
|
|
self.push_triangle(self.start, self.current, point, PathVertexKind::Solid);
|
2021-03-29 17:50:38 +00:00
|
|
|
}
|
|
|
|
|
2021-03-29 23:28:35 +00:00
|
|
|
self.push_triangle(self.current, ctrl, point, PathVertexKind::Quadratic);
|
2021-03-29 17:50:38 +00:00
|
|
|
self.current = point;
|
|
|
|
}
|
|
|
|
|
2021-08-03 19:48:58 +00:00
|
|
|
pub fn build(mut self, color: Color, clip_bounds: Option<RectF>) -> Path {
|
2021-04-02 11:49:44 +00:00
|
|
|
if let Some(clip_bounds) = clip_bounds {
|
2022-08-10 21:39:24 +00:00
|
|
|
self.bounds = self.bounds.intersection(clip_bounds).unwrap_or_default();
|
2021-04-02 11:49:44 +00:00
|
|
|
}
|
2021-03-29 23:28:35 +00:00
|
|
|
Path {
|
2021-03-30 04:46:26 +00:00
|
|
|
bounds: self.bounds,
|
|
|
|
color,
|
2021-03-29 23:28:35 +00:00
|
|
|
vertices: self.vertices,
|
|
|
|
}
|
2021-03-29 17:50:38 +00:00
|
|
|
}
|
|
|
|
|
2021-03-29 23:28:35 +00:00
|
|
|
fn push_triangle(&mut self, a: Vector2F, b: Vector2F, c: Vector2F, kind: PathVertexKind) {
|
2021-03-30 04:46:26 +00:00
|
|
|
if self.vertices.is_empty() {
|
|
|
|
self.bounds = RectF::new(a, Vector2F::zero());
|
|
|
|
}
|
|
|
|
self.bounds = self.bounds.union_point(a).union_point(b).union_point(c);
|
|
|
|
|
2021-03-29 17:50:38 +00:00
|
|
|
match kind {
|
2021-03-29 23:28:35 +00:00
|
|
|
PathVertexKind::Solid => {
|
|
|
|
self.vertices.push(PathVertex {
|
2021-03-29 17:50:38 +00:00
|
|
|
xy_position: a,
|
|
|
|
st_position: vec2f(0., 1.),
|
|
|
|
});
|
2021-03-29 23:28:35 +00:00
|
|
|
self.vertices.push(PathVertex {
|
2021-03-29 17:50:38 +00:00
|
|
|
xy_position: b,
|
|
|
|
st_position: vec2f(0., 1.),
|
|
|
|
});
|
2021-03-29 23:28:35 +00:00
|
|
|
self.vertices.push(PathVertex {
|
2021-03-29 17:50:38 +00:00
|
|
|
xy_position: c,
|
|
|
|
st_position: vec2f(0., 1.),
|
|
|
|
});
|
|
|
|
}
|
2021-03-29 23:28:35 +00:00
|
|
|
PathVertexKind::Quadratic => {
|
|
|
|
self.vertices.push(PathVertex {
|
2021-03-29 17:50:38 +00:00
|
|
|
xy_position: a,
|
|
|
|
st_position: vec2f(0., 0.),
|
|
|
|
});
|
2021-03-29 23:28:35 +00:00
|
|
|
self.vertices.push(PathVertex {
|
2021-03-29 17:50:38 +00:00
|
|
|
xy_position: b,
|
|
|
|
st_position: vec2f(0.5, 0.),
|
|
|
|
});
|
2021-03-29 23:28:35 +00:00
|
|
|
self.vertices.push(PathVertex {
|
2021-03-29 17:50:38 +00:00
|
|
|
xy_position: c,
|
|
|
|
st_position: vec2f(1., 1.),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-07 05:50:13 +00:00
|
|
|
|
2021-08-03 19:48:58 +00:00
|
|
|
pub fn deserialize_vec2f<'de, D>(deserializer: D) -> Result<Vector2F, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let [x, y]: [f32; 2] = Deserialize::deserialize(deserializer)?;
|
|
|
|
Ok(vec2f(x, y))
|
|
|
|
}
|
|
|
|
|
2021-04-07 05:50:13 +00:00
|
|
|
impl ToJson for Vector2F {
|
|
|
|
fn to_json(&self) -> serde_json::Value {
|
|
|
|
json!([self.x(), self.y()])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ToJson for RectF {
|
|
|
|
fn to_json(&self) -> serde_json::Value {
|
|
|
|
json!({"origin": self.origin().to_json(), "size": self.size().to_json()})
|
|
|
|
}
|
|
|
|
}
|
2023-08-16 04:02:56 +00:00
|
|
|
|
2023-08-18 07:03:46 +00:00
|
|
|
#[derive(Refineable)]
|
2023-08-20 01:51:17 +00:00
|
|
|
pub struct Point<T: Clone + Default> {
|
2023-08-16 04:02:56 +00:00
|
|
|
pub x: T,
|
|
|
|
pub y: T,
|
|
|
|
}
|
|
|
|
|
2023-08-20 01:51:17 +00:00
|
|
|
impl<T: Clone + Default> Clone for Point<T> {
|
2023-08-18 07:03:46 +00:00
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Self {
|
|
|
|
x: self.x.clone(),
|
|
|
|
y: self.y.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-20 01:51:17 +00:00
|
|
|
impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
|
2023-08-16 04:02:56 +00:00
|
|
|
fn into(self) -> taffy::geometry::Point<T> {
|
|
|
|
taffy::geometry::Point {
|
|
|
|
x: self.x,
|
|
|
|
y: self.y,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-18 07:03:46 +00:00
|
|
|
#[derive(Clone, Refineable)]
|
2023-08-20 01:51:17 +00:00
|
|
|
pub struct Size<T: Clone + Default> {
|
2023-08-16 04:02:56 +00:00
|
|
|
pub width: T,
|
|
|
|
pub height: T,
|
|
|
|
}
|
|
|
|
|
2023-08-20 01:51:17 +00:00
|
|
|
impl<S, T: Clone + Default> From<taffy::geometry::Size<S>> for Size<T>
|
2023-08-16 04:02:56 +00:00
|
|
|
where
|
|
|
|
S: Into<T>,
|
|
|
|
{
|
|
|
|
fn from(value: taffy::geometry::Size<S>) -> Self {
|
|
|
|
Self {
|
|
|
|
width: value.width.into(),
|
|
|
|
height: value.height.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-20 01:51:17 +00:00
|
|
|
impl<S, T: Clone + Default> Into<taffy::geometry::Size<S>> for Size<T>
|
2023-08-16 04:02:56 +00:00
|
|
|
where
|
|
|
|
T: Into<S>,
|
|
|
|
{
|
|
|
|
fn into(self) -> taffy::geometry::Size<S> {
|
|
|
|
taffy::geometry::Size {
|
|
|
|
width: self.width.into(),
|
|
|
|
height: self.height.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Size<DefinedLength> {
|
|
|
|
pub const fn zero() -> Self {
|
|
|
|
Self {
|
|
|
|
width: DefinedLength::Pixels(0.),
|
|
|
|
height: DefinedLength::Pixels(0.),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Size<taffy::style::LengthPercentage> {
|
|
|
|
taffy::geometry::Size {
|
|
|
|
width: self.width.to_taffy(rem_size),
|
|
|
|
height: self.height.to_taffy(rem_size),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Size<Length> {
|
|
|
|
pub const fn auto() -> Self {
|
|
|
|
Self {
|
|
|
|
width: Length::Auto,
|
|
|
|
height: Length::Auto,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_taffy<T: From<taffy::prelude::LengthPercentageAuto>>(
|
|
|
|
&self,
|
|
|
|
rem_size: f32,
|
|
|
|
) -> taffy::geometry::Size<T> {
|
|
|
|
taffy::geometry::Size {
|
|
|
|
width: self.width.to_taffy(rem_size).into(),
|
|
|
|
height: self.height.to_taffy(rem_size).into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-18 07:03:46 +00:00
|
|
|
#[derive(Clone, Default, Refineable)]
|
2023-08-20 01:51:17 +00:00
|
|
|
pub struct Edges<T: Clone + Default> {
|
2023-08-16 04:02:56 +00:00
|
|
|
pub top: T,
|
|
|
|
pub right: T,
|
|
|
|
pub bottom: T,
|
|
|
|
pub left: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Edges<DefinedLength> {
|
|
|
|
pub const fn zero() -> Self {
|
|
|
|
Self {
|
|
|
|
top: DefinedLength::Pixels(0.0),
|
|
|
|
right: DefinedLength::Pixels(0.0),
|
|
|
|
bottom: DefinedLength::Pixels(0.0),
|
|
|
|
left: DefinedLength::Pixels(0.0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
|
|
|
|
taffy::geometry::Rect {
|
|
|
|
top: self.top.to_taffy(rem_size),
|
|
|
|
right: self.right.to_taffy(rem_size),
|
|
|
|
bottom: self.bottom.to_taffy(rem_size),
|
|
|
|
left: self.left.to_taffy(rem_size),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Edges<Length> {
|
|
|
|
pub const fn auto() -> Self {
|
|
|
|
Self {
|
|
|
|
top: Length::Auto,
|
|
|
|
right: Length::Auto,
|
|
|
|
bottom: Length::Auto,
|
|
|
|
left: Length::Auto,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const fn zero() -> Self {
|
|
|
|
Self {
|
|
|
|
top: Length::Defined(DefinedLength::Pixels(0.0)),
|
|
|
|
right: Length::Defined(DefinedLength::Pixels(0.0)),
|
|
|
|
bottom: Length::Defined(DefinedLength::Pixels(0.0)),
|
|
|
|
left: Length::Defined(DefinedLength::Pixels(0.0)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_taffy(
|
|
|
|
&self,
|
|
|
|
rem_size: f32,
|
|
|
|
) -> taffy::geometry::Rect<taffy::style::LengthPercentageAuto> {
|
|
|
|
taffy::geometry::Rect {
|
|
|
|
top: self.top.to_taffy(rem_size),
|
|
|
|
right: self.right.to_taffy(rem_size),
|
|
|
|
bottom: self.bottom.to_taffy(rem_size),
|
|
|
|
left: self.left.to_taffy(rem_size),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A non-auto length that can be defined in pixels, rems, or percent of parent.
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub enum DefinedLength {
|
|
|
|
Pixels(f32),
|
|
|
|
Rems(f32),
|
|
|
|
Percent(f32), // 0. - 100.
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DefinedLength {
|
|
|
|
fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
|
|
|
|
match self {
|
|
|
|
DefinedLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
|
|
|
|
DefinedLength::Rems(rems) => taffy::style::LengthPercentage::Length(rems * rem_size),
|
|
|
|
DefinedLength::Percent(percent) => {
|
|
|
|
taffy::style::LengthPercentage::Percent(*percent / 100.)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-18 07:03:46 +00:00
|
|
|
impl Default for DefinedLength {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::Pixels(0.)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-16 04:02:56 +00:00
|
|
|
/// A length that can be defined in pixels, rems, percent of parent, or auto.
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub enum Length {
|
|
|
|
Defined(DefinedLength),
|
|
|
|
Auto,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn auto() -> Length {
|
|
|
|
Length::Auto
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn percent(percent: f32) -> DefinedLength {
|
|
|
|
DefinedLength::Percent(percent)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rems(rems: f32) -> DefinedLength {
|
|
|
|
DefinedLength::Rems(rems)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pixels(pixels: f32) -> DefinedLength {
|
|
|
|
DefinedLength::Pixels(pixels)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Length {
|
|
|
|
pub fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
|
|
|
|
match self {
|
|
|
|
Length::Defined(length) => length.to_taffy(rem_size).into(),
|
|
|
|
Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DefinedLength> for Length {
|
|
|
|
fn from(value: DefinedLength) -> Self {
|
|
|
|
Length::Defined(value)
|
|
|
|
}
|
|
|
|
}
|
2023-08-18 07:03:46 +00:00
|
|
|
|
|
|
|
impl Default for Length {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::Defined(DefinedLength::default())
|
|
|
|
}
|
|
|
|
}
|