mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 13:18:11 +08:00
committed by
Keavon Chambers
parent
31fb0d7148
commit
916d10980d
@@ -1,6 +1,6 @@
|
||||
use crate::consts::{F64PRECISE, RAY_FUDGE_FACTOR};
|
||||
use crate::intersection::{intersections, line_curve_intersections, valid_t, Intersect, Origin};
|
||||
use crate::layers::simple_shape::Shape;
|
||||
use crate::layers::shape_layer::ShapeLayer;
|
||||
use crate::layers::style::PathStyle;
|
||||
|
||||
use kurbo::{BezPath, CubicBez, Line, ParamCurve, ParamCurveArclen, ParamCurveArea, ParamCurveExtrema, PathEl, PathSeg, Point, QuadBez, Rect};
|
||||
@@ -377,7 +377,7 @@ impl PathGraph {
|
||||
cycles
|
||||
}
|
||||
|
||||
pub fn get_shape(&self, cycle: &Cycle, style: &PathStyle) -> Shape {
|
||||
pub fn get_shape(&self, cycle: &Cycle, style: &PathStyle) -> ShapeLayer {
|
||||
let mut curve = Vec::new();
|
||||
let vertices = cycle.vertices();
|
||||
for index in 1..vertices.len() {
|
||||
@@ -385,7 +385,7 @@ impl PathGraph {
|
||||
concat_paths(&mut curve, &self.edge(vertices[index - 1].0, vertices[index].0, vertices[index].1).unwrap().curve);
|
||||
}
|
||||
curve.push(PathEl::ClosePath);
|
||||
Shape::from_bez_path(BezPath::from_vec(curve), *style, false)
|
||||
ShapeLayer::from_bez_path(BezPath::from_vec(curve), *style, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ pub fn subdivide_path_seg(p: &PathSeg, t_values: &mut [f64]) -> Vec<Option<PathS
|
||||
|
||||
// TODO: check if shapes are filled
|
||||
// TODO: Bug: shape with at least two subpaths and comprised of many unions sometimes has erroneous movetos embedded in edges
|
||||
pub fn boolean_operation(select: BooleanOperation, mut alpha: Shape, mut beta: Shape) -> Result<Vec<Shape>, BooleanOperationError> {
|
||||
pub fn boolean_operation(select: BooleanOperation, mut alpha: ShapeLayer, mut beta: ShapeLayer) -> Result<Vec<ShapeLayer>, BooleanOperationError> {
|
||||
if alpha.path.is_empty() || beta.path.is_empty() {
|
||||
return Err(BooleanOperationError::InvalidSelection);
|
||||
}
|
||||
@@ -618,7 +618,7 @@ pub fn bounding_box(curve: &BezPath) -> Rect {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn collect_shapes<'a, F, G>(graph: &PathGraph, cycles: &mut Vec<Cycle>, predicate: F, style: G) -> Result<Vec<Shape>, BooleanOperationError>
|
||||
fn collect_shapes<'a, F, G>(graph: &PathGraph, cycles: &mut Vec<Cycle>, predicate: F, style: G) -> Result<Vec<ShapeLayer>, BooleanOperationError>
|
||||
where
|
||||
F: Fn(Direction) -> bool,
|
||||
G: Fn(Direction) -> &'a PathStyle,
|
||||
|
||||
+25
-25
@@ -1,11 +1,11 @@
|
||||
use crate::boolean_ops::boolean_operation;
|
||||
use crate::intersection::Quad;
|
||||
use crate::layers;
|
||||
use crate::layers::folder::Folder;
|
||||
use crate::layers::folder_layer::FolderLayer;
|
||||
use crate::layers::layer_info::{Layer, LayerData, LayerDataType};
|
||||
use crate::layers::simple_shape::Shape;
|
||||
use crate::layers::shape_layer::ShapeLayer;
|
||||
use crate::layers::style::ViewMode;
|
||||
use crate::layers::text::Text;
|
||||
use crate::layers::text_layer::TextLayer;
|
||||
use crate::{DocumentError, DocumentResponse, Operation};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -29,7 +29,7 @@ pub struct Document {
|
||||
impl Default for Document {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
root: Layer::new(LayerDataType::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array()),
|
||||
root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()),
|
||||
state_identifier: DefaultHasher::new(),
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ impl Document {
|
||||
|
||||
/// Returns a reference to the requested folder. Fails if the path does not exist,
|
||||
/// or if the requested layer is not of type folder.
|
||||
pub fn folder(&self, path: impl AsRef<[LayerId]>) -> Result<&Folder, DocumentError> {
|
||||
pub fn folder(&self, path: impl AsRef<[LayerId]>) -> Result<&FolderLayer, DocumentError> {
|
||||
let mut root = &self.root;
|
||||
for id in path.as_ref() {
|
||||
root = root.as_folder()?.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.as_ref().into()))?;
|
||||
@@ -71,7 +71,7 @@ impl Document {
|
||||
/// Returns a mutable reference to the requested folder. Fails if the path does not exist,
|
||||
/// or if the requested layer is not of type folder.
|
||||
/// If you manually edit the folder you have to set the cache_dirty flag yourself.
|
||||
fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> {
|
||||
fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut FolderLayer, DocumentError> {
|
||||
let mut root = &mut self.root;
|
||||
for id in path {
|
||||
root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
|
||||
@@ -99,8 +99,8 @@ impl Document {
|
||||
|
||||
/// Returns vector `Shape`s for each specified in `paths`.
|
||||
/// If any path is not a shape, or does not exist, `DocumentError::InvalidPath` is returned.
|
||||
fn transformed_shapes(&self, paths: &[Vec<LayerId>]) -> Result<Vec<Shape>, DocumentError> {
|
||||
let mut shapes: Vec<Shape> = Vec::new();
|
||||
fn transformed_shapes(&self, paths: &[Vec<LayerId>]) -> Result<Vec<ShapeLayer>, DocumentError> {
|
||||
let mut shapes: Vec<ShapeLayer> = Vec::new();
|
||||
let undo_viewport = self.root.transform.inverse();
|
||||
for path in paths {
|
||||
match (self.multiply_transforms(path), &self.layer(path)?.data) {
|
||||
@@ -263,7 +263,7 @@ impl Document {
|
||||
}
|
||||
|
||||
/// Visit each layer recursively, applies modify_shape to each non-overlay Shape
|
||||
pub fn visit_all_shapes<F: FnMut(&mut Shape)>(layer: &mut Layer, modify_shape: &mut F) -> bool {
|
||||
pub fn visit_all_shapes<F: FnMut(&mut ShapeLayer)>(layer: &mut Layer, modify_shape: &mut F) -> bool {
|
||||
match layer.data {
|
||||
LayerDataType::Shape(ref mut shape) => {
|
||||
modify_shape(shape);
|
||||
@@ -434,14 +434,14 @@ impl Document {
|
||||
|
||||
let responses = match &operation {
|
||||
Operation::AddEllipse { path, insert_index, transform, style } => {
|
||||
let layer = Layer::new(LayerDataType::Shape(Shape::ellipse(*style)), *transform);
|
||||
let layer = Layer::new(LayerDataType::Shape(ShapeLayer::ellipse(*style)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(path)].concat())
|
||||
}
|
||||
Operation::AddOverlayEllipse { path, transform, style } => {
|
||||
let mut ellipse = Shape::ellipse(*style);
|
||||
let mut ellipse = ShapeLayer::ellipse(*style);
|
||||
ellipse.render_index = -1;
|
||||
|
||||
let layer = Layer::new(LayerDataType::Shape(ellipse), *transform);
|
||||
@@ -450,14 +450,14 @@ impl Document {
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }]].concat())
|
||||
}
|
||||
Operation::AddRect { path, insert_index, transform, style } => {
|
||||
let layer = Layer::new(LayerDataType::Shape(Shape::rectangle(*style)), *transform);
|
||||
let layer = Layer::new(LayerDataType::Shape(ShapeLayer::rectangle(*style)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(path)].concat())
|
||||
}
|
||||
Operation::AddOverlayRect { path, transform, style } => {
|
||||
let mut rect = Shape::rectangle(*style);
|
||||
let mut rect = ShapeLayer::rectangle(*style);
|
||||
rect.render_index = -1;
|
||||
|
||||
let layer = Layer::new(LayerDataType::Shape(rect), *transform);
|
||||
@@ -466,14 +466,14 @@ impl Document {
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }]].concat())
|
||||
}
|
||||
Operation::AddLine { path, insert_index, transform, style } => {
|
||||
let layer = Layer::new(LayerDataType::Shape(Shape::line(*style)), *transform);
|
||||
let layer = Layer::new(LayerDataType::Shape(ShapeLayer::line(*style)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(path)].concat())
|
||||
}
|
||||
Operation::AddOverlayLine { path, transform, style } => {
|
||||
let mut line = Shape::line(*style);
|
||||
let mut line = ShapeLayer::line(*style);
|
||||
line.render_index = -1;
|
||||
|
||||
let layer = Layer::new(LayerDataType::Shape(line), *transform);
|
||||
@@ -490,7 +490,7 @@ impl Document {
|
||||
style,
|
||||
size,
|
||||
} => {
|
||||
let layer = Layer::new(LayerDataType::Text(Text::new(text.clone(), *style, *size)), *transform);
|
||||
let layer = Layer::new(LayerDataType::Text(TextLayer::new(text.clone(), *style, *size)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
@@ -514,14 +514,14 @@ impl Document {
|
||||
style,
|
||||
sides,
|
||||
} => {
|
||||
let layer = Layer::new(LayerDataType::Shape(Shape::ngon(*sides, *style)), *transform);
|
||||
let layer = Layer::new(LayerDataType::Shape(ShapeLayer::ngon(*sides, *style)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(path)].concat())
|
||||
}
|
||||
Operation::AddOverlayShape { path, style, bez_path, closed } => {
|
||||
let mut shape = Shape::from_bez_path(bez_path.clone(), *style, *closed);
|
||||
let mut shape = ShapeLayer::from_bez_path(bez_path.clone(), *style, *closed);
|
||||
shape.render_index = -1;
|
||||
|
||||
let layer = Layer::new(LayerDataType::Shape(shape), DAffine2::IDENTITY.to_cols_array());
|
||||
@@ -537,7 +537,7 @@ impl Document {
|
||||
bez_path,
|
||||
closed,
|
||||
} => {
|
||||
let shape = Shape::from_bez_path(bez_path.clone(), *style, *closed);
|
||||
let shape = ShapeLayer::from_bez_path(bez_path.clone(), *style, *closed);
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(shape), *transform), *insert_index)?;
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }]].concat())
|
||||
}
|
||||
@@ -549,7 +549,7 @@ impl Document {
|
||||
style,
|
||||
} => {
|
||||
let points: Vec<glam::DVec2> = points.iter().map(|&it| it.into()).collect();
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(Shape::poly_line(points, *style)), *transform), *insert_index)?;
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(ShapeLayer::poly_line(points, *style)), *transform), *insert_index)?;
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(path)].concat())
|
||||
}
|
||||
Operation::BooleanOperation { operation, selected } => {
|
||||
@@ -588,11 +588,11 @@ impl Document {
|
||||
style,
|
||||
} => {
|
||||
let points: Vec<glam::DVec2> = points.iter().map(|&it| it.into()).collect();
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(Shape::spline(points, *style)), *transform), *insert_index)?;
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(ShapeLayer::spline(points, *style)), *transform), *insert_index)?;
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(path)].concat())
|
||||
}
|
||||
Operation::DeleteLayer { path } => {
|
||||
fn aggregate_deletions(folder: &Folder, path: &mut Vec<LayerId>, responses: &mut Vec<DocumentResponse>) {
|
||||
fn aggregate_deletions(folder: &FolderLayer, path: &mut Vec<LayerId>, responses: &mut Vec<DocumentResponse>) {
|
||||
for (id, layer) in folder.layer_ids.iter().zip(folder.layers()) {
|
||||
path.push(*id);
|
||||
responses.push(DocumentResponse::DeletedLayer { path: path.clone() });
|
||||
@@ -623,7 +623,7 @@ impl Document {
|
||||
folder.add_layer(layer.clone(), Some(layer_id), *insert_index).ok_or(DocumentError::IndexOutOfBounds)?;
|
||||
self.mark_as_dirty(destination_path)?;
|
||||
|
||||
fn aggregate_insertions(folder: &Folder, path: &mut Vec<LayerId>, responses: &mut Vec<DocumentResponse>) {
|
||||
fn aggregate_insertions(folder: &FolderLayer, path: &mut Vec<LayerId>, responses: &mut Vec<DocumentResponse>) {
|
||||
for (id, layer) in folder.layer_ids.iter().zip(folder.layers()) {
|
||||
path.push(*id);
|
||||
responses.push(DocumentResponse::CreatedLayer { path: path.clone() });
|
||||
@@ -666,7 +666,7 @@ impl Document {
|
||||
Some(vec![LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::CreateFolder { path } => {
|
||||
self.set_layer(path, Layer::new(LayerDataType::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array()), -1)?;
|
||||
self.set_layer(path, Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()), -1)?;
|
||||
self.mark_as_dirty(path)?;
|
||||
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(path)].concat())
|
||||
@@ -705,7 +705,7 @@ impl Document {
|
||||
|
||||
if let LayerDataType::Text(t) = &mut self.layer_mut(path)?.data {
|
||||
let bezpath = t.to_bez_path();
|
||||
self.layer_mut(path)?.data = layers::layer_info::LayerDataType::Shape(Shape::from_bez_path(bezpath, t.style, true));
|
||||
self.layer_mut(path)?.data = layers::layer_info::LayerDataType::Shape(ShapeLayer::from_bez_path(bezpath, t.style, true));
|
||||
}
|
||||
|
||||
if let LayerDataType::Shape(shape) = &mut self.layer_mut(path)?.data {
|
||||
|
||||
@@ -8,13 +8,13 @@ use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Default)]
|
||||
pub struct Folder {
|
||||
pub struct FolderLayer {
|
||||
next_assignment_id: LayerId,
|
||||
pub layer_ids: Vec<LayerId>,
|
||||
layers: Vec<Layer>,
|
||||
}
|
||||
|
||||
impl LayerData for Folder {
|
||||
impl LayerData for FolderLayer {
|
||||
fn render(&mut self, svg: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode) {
|
||||
for layer in &mut self.layers {
|
||||
let _ = writeln!(svg, "{}", layer.render(transforms, view_mode));
|
||||
@@ -37,7 +37,7 @@ impl LayerData for Folder {
|
||||
}
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
impl FolderLayer {
|
||||
/// When a insertion id is provided, try to insert the layer with the given id.
|
||||
/// If that id is already used, return None.
|
||||
/// When no insertion id is provided, search for the next free id and insert it with that.
|
||||
@@ -109,7 +109,7 @@ impl Folder {
|
||||
self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into()))
|
||||
}
|
||||
|
||||
pub fn folder(&self, id: LayerId) -> Option<&Folder> {
|
||||
pub fn folder(&self, id: LayerId) -> Option<&FolderLayer> {
|
||||
match self.layer(id) {
|
||||
Some(Layer {
|
||||
data: LayerDataType::Folder(folder), ..
|
||||
@@ -118,7 +118,7 @@ impl Folder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn folder_mut(&mut self, id: LayerId) -> Option<&mut Folder> {
|
||||
pub fn folder_mut(&mut self, id: LayerId) -> Option<&mut FolderLayer> {
|
||||
match self.layer_mut(id) {
|
||||
Some(Layer {
|
||||
data: LayerDataType::Folder(folder), ..
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::blend_mode::BlendMode;
|
||||
use super::folder::Folder;
|
||||
use super::simple_shape::Shape;
|
||||
use super::folder_layer::FolderLayer;
|
||||
use super::shape_layer::ShapeLayer;
|
||||
use super::style::ViewMode;
|
||||
use super::text::Text;
|
||||
use super::text_layer::TextLayer;
|
||||
use crate::intersection::Quad;
|
||||
use crate::DocumentError;
|
||||
use crate::LayerId;
|
||||
@@ -13,9 +13,9 @@ use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum LayerDataType {
|
||||
Folder(Folder),
|
||||
Shape(Shape),
|
||||
Text(Text),
|
||||
Folder(FolderLayer),
|
||||
Shape(ShapeLayer),
|
||||
Text(TextLayer),
|
||||
}
|
||||
|
||||
impl LayerDataType {
|
||||
@@ -149,28 +149,28 @@ impl Layer {
|
||||
self.current_bounding_box_with_transform(self.transform)
|
||||
}
|
||||
|
||||
pub fn as_folder_mut(&mut self) -> Result<&mut Folder, DocumentError> {
|
||||
pub fn as_folder_mut(&mut self) -> Result<&mut FolderLayer, DocumentError> {
|
||||
match &mut self.data {
|
||||
LayerDataType::Folder(f) => Ok(f),
|
||||
_ => Err(DocumentError::NotAFolder),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_folder(&self) -> Result<&Folder, DocumentError> {
|
||||
pub fn as_folder(&self) -> Result<&FolderLayer, DocumentError> {
|
||||
match &self.data {
|
||||
LayerDataType::Folder(f) => Ok(f),
|
||||
_ => Err(DocumentError::NotAFolder),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_text_mut(&mut self) -> Result<&mut Text, DocumentError> {
|
||||
pub fn as_text_mut(&mut self) -> Result<&mut TextLayer, DocumentError> {
|
||||
match &mut self.data {
|
||||
LayerDataType::Text(t) => Ok(t),
|
||||
_ => Err(DocumentError::NotText),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_text(&self) -> Result<&Text, DocumentError> {
|
||||
pub fn as_text(&self) -> Result<&TextLayer, DocumentError> {
|
||||
match &self.data {
|
||||
LayerDataType::Text(t) => Ok(t),
|
||||
_ => Err(DocumentError::NotText),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pub mod blend_mode;
|
||||
pub mod folder;
|
||||
pub mod folder_layer;
|
||||
pub mod layer_info;
|
||||
pub mod simple_shape;
|
||||
pub mod shape_layer;
|
||||
pub mod style;
|
||||
pub mod text;
|
||||
pub mod text_layer;
|
||||
|
||||
@@ -13,14 +13,14 @@ fn glam_to_kurbo(transform: DAffine2) -> Affine {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Shape {
|
||||
pub struct ShapeLayer {
|
||||
pub path: BezPath,
|
||||
pub style: style::PathStyle,
|
||||
pub render_index: i32,
|
||||
pub closed: bool,
|
||||
}
|
||||
|
||||
impl LayerData for Shape {
|
||||
impl LayerData for ShapeLayer {
|
||||
fn render(&mut self, svg: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode) {
|
||||
let mut path = self.path.clone();
|
||||
let transform = self.transform(transforms, view_mode);
|
||||
@@ -60,7 +60,7 @@ impl LayerData for Shape {
|
||||
}
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
impl ShapeLayer {
|
||||
pub fn transform(&self, transforms: &[DAffine2], mode: ViewMode) -> DAffine2 {
|
||||
let start = match (mode, self.render_index) {
|
||||
(ViewMode::Outline, _) => 0,
|
||||
@@ -15,7 +15,7 @@ fn glam_to_kurbo(transform: DAffine2) -> Affine {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Text {
|
||||
pub struct TextLayer {
|
||||
pub text: String,
|
||||
pub style: style::PathStyle,
|
||||
pub size: f64,
|
||||
@@ -26,7 +26,7 @@ pub struct Text {
|
||||
cached_path: Option<BezPath>,
|
||||
}
|
||||
|
||||
impl LayerData for Text {
|
||||
impl LayerData for TextLayer {
|
||||
fn render(&mut self, svg: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode) {
|
||||
let transform = self.transform(transforms, view_mode);
|
||||
let inverse = transform.inverse();
|
||||
@@ -84,7 +84,7 @@ impl LayerData for Text {
|
||||
}
|
||||
}
|
||||
|
||||
impl Text {
|
||||
impl TextLayer {
|
||||
pub fn transform(&self, transforms: &[DAffine2], mode: ViewMode) -> DAffine2 {
|
||||
let start = match mode {
|
||||
ViewMode::Outline => 0,
|
||||
Reference in New Issue
Block a user