Skip to content

Add a movable canvas with matricies #175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Jun 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4d638c6
Convert polygon and rectangle tool to kurbo::BezPath
0HyperCube Jun 10, 2021
4ce2f7e
Merge branch 'master' into matrix-transforms
0HyperCube Jun 10, 2021
8450cdd
Add glam
0HyperCube Jun 10, 2021
e8145f3
Add affine transform to elipse and remove circle
0HyperCube Jun 10, 2021
436fd2a
Format
0HyperCube Jun 10, 2021
fa8189e
Add svg group and add matrix for group
0HyperCube Jun 11, 2021
3b9dd7b
Convert all operations to use matricies
0HyperCube Jun 13, 2021
47ee67f
Work uses same transform as root
0HyperCube Jun 13, 2021
854715d
Format
0HyperCube Jun 13, 2021
b737e27
Frontend fixed to render changes to working colors when changed from …
akshay1992kalbhor Jun 12, 2021
df1a95e
Remove early sample "greet" code
Keavon Jun 12, 2021
70c7917
Add a contributing section to the project README
Keavon Jun 13, 2021
00aa790
Merge branch 'master' into matrix-transforms
0HyperCube Jun 13, 2021
ba1d4f4
Add moving document around
0HyperCube Jun 13, 2021
6b664f9
Add document transform for tools
0HyperCube Jun 13, 2021
89b6263
Merge branch 'master' into matrix-transforms
0HyperCube Jun 13, 2021
d10f74a
Update to GraphiteEditor's fork
0HyperCube Jun 13, 2021
8991094
Use write in foreach for rendering group / folder
0HyperCube Jun 13, 2021
ecd3f34
Add missing TranslateDown action
0HyperCube Jun 13, 2021
dd8b048
Use points for line operation
0HyperCube Jun 13, 2021
ce66e35
Format
0HyperCube Jun 13, 2021
77e089c
Add todo to change to shape's aspect ratio
0HyperCube Jun 15, 2021
904abee
Remove empty if
0HyperCube Jun 15, 2021
30a1177
Initial pass at refactor
0HyperCube Jun 16, 2021
9898821
Merge branch 'master' into matrix-transforms
0HyperCube Jun 16, 2021
137682f
Fix polyline test
0HyperCube Jun 16, 2021
6a250f5
Merge branch 'master' into matrix-transforms
0HyperCube Jun 20, 2021
0cfa296
Merge branch 'master' into matrix-transforms
0HyperCube Jun 24, 2021
f4ef8fc
Merge branch 'master' into matrix-transforms
0HyperCube Jun 25, 2021
b015239
Use document message to modify document transform
0HyperCube Jun 25, 2021
b56a64a
Messages -> Operations
0HyperCube Jun 25, 2021
a1bbc9c
Transform layer
0HyperCube Jun 25, 2021
b715fd4
Format
0HyperCube Jun 25, 2021
25e5aae
Use DAffine2::IDENTITY
0HyperCube Jun 25, 2021
6be462f
Clean up kurbo generation for line and rect
0HyperCube Jun 25, 2021
19a164d
Use .into for rectangle points
0HyperCube Jun 26, 2021
0b10a60
Rename cols to transform
0HyperCube Jun 26, 2021
663415b
Rename other cols to transform
0HyperCube Jun 26, 2021
db6cc50
Add todo for into_iter
0HyperCube Jun 26, 2021
63e009d
Remove unnecessary clone
0HyperCube Jun 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion core/document/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ license = "Apache-2.0"

[dependencies]
log = "0.4"
kurbo = "0.8.0"
# TODO: Swich to kurbo release when derive `PartialEq` is available for BezPath
#kurbo = "0.8.0"
kurbo = {git="https://github.com/GraphiteEditor/kurbo.git", branch="bezpath-partial-eq"}
serde = { version = "1.0", features = ["derive"] }
glam = "0.16"
115 changes: 50 additions & 65 deletions core/document/src/document.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use glam::DAffine2;

use crate::{
layers::{self, Folder, Layer, LayerData, LayerDataTypes, Line, PolyLine, Rect, Shape},
layers::{self, style::PathStyle, Folder, Layer, LayerDataTypes, Line, PolyLine, Rect, Shape},
DocumentError, DocumentResponse, LayerId, Operation,
};

#[derive(Debug, Clone, PartialEq)]
pub struct Document {
pub root: layers::Folder,
pub work: Folder,
pub root: Layer,
pub work: Layer,
pub work_mount_path: Vec<LayerId>,
pub work_operations: Vec<Operation>,
pub work_mounted: bool,
Expand All @@ -15,8 +17,8 @@ pub struct Document {
impl Default for Document {
fn default() -> Self {
Self {
root: Folder::default(),
work: Folder::default(),
root: Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default()),
work: Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default()),
work_mount_path: Vec::new(),
work_operations: Vec::new(),
work_mounted: false,
Expand All @@ -41,8 +43,11 @@ impl Document {
return;
}
if path.as_slice() == self.work_mount_path {
self.document_folder_mut(path).unwrap().render(svg);
self.work.render(svg);
// TODO: Handle if mounted in nested folders
let transform = self.document_folder(path).unwrap().transform;
self.document_folder_mut(path).unwrap().render_as_folder(svg);
self.work.transform = transform;
self.work.render_as_folder(svg);
path.pop();
}
let ids = self.folder(path).unwrap().layer_ids.clone();
Expand All @@ -68,10 +73,10 @@ impl Document {
/// This function respects mounted folders and will thus not contain the layers already
/// present in the document if a temporary folder is mounted on top.
pub fn folder(&self, mut path: &[LayerId]) -> Result<&Folder, DocumentError> {
let mut root = &self.root;
let mut root = self.root.as_folder()?;
if self.is_mounted(self.work_mount_path.as_slice(), path) {
path = &path[self.work_mount_path.len()..];
root = &self.work;
root = self.work.as_folder()?;
}
for id in path {
root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?;
Expand All @@ -87,9 +92,9 @@ impl Document {
pub fn folder_mut(&mut self, mut path: &[LayerId]) -> Result<&mut Folder, DocumentError> {
let mut root = if self.is_mounted(self.work_mount_path.as_slice(), path) {
path = &path[self.work_mount_path.len()..];
&mut self.work
self.work.as_folder_mut()?
} else {
&mut self.root
self.root.as_folder_mut()?
};
for id in path {
root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?;
Expand All @@ -101,10 +106,10 @@ impl Document {
/// or if the requested layer is not of type folder.
/// This function does **not** respect mounted folders and will always return the current
/// state of the document, disregarding any temporary modifications.
pub fn document_folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> {
pub fn document_folder(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> {
let mut root = &self.root;
for id in path {
root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?;
root = root.as_folder()?.layer(*id).ok_or(DocumentError::LayerNotFound)?;
}
Ok(root)
}
Expand All @@ -114,10 +119,10 @@ impl Document {
/// This function does **not** respect mounted folders and will always return the current
/// state of the document, disregarding any temporary modifications.
/// If you manually edit the folder you have to set the cache_dirty flag yourself.
pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> {
pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Layer, DocumentError> {
let mut root = &mut self.root;
for id in path {
root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?;
root = root.as_folder_mut()?.layer_mut(*id).ok_or(DocumentError::LayerNotFound)?;
}
Ok(root)
}
Expand All @@ -137,7 +142,7 @@ impl Document {

/// Replaces the layer at the specified `path` with `layer`.
pub fn set_layer(&mut self, path: &[LayerId], layer: Layer) -> Result<(), DocumentError> {
let mut folder = &mut self.root;
let mut folder = self.root.as_folder_mut()?;
if let Ok((path, id)) = split_path(path) {
self.layer_mut(path)?.cache_dirty = true;
folder = self.folder_mut(path)?;
Expand All @@ -163,81 +168,54 @@ impl Document {
pub fn delete(&mut self, path: &[LayerId]) -> Result<(), DocumentError> {
let (path, id) = split_path(path)?;
let _ = self.layer_mut(path).map(|x| x.cache_dirty = true);
self.document_folder_mut(path)?.remove_layer(id)?;
self.document_folder_mut(path)?.as_folder_mut()?.remove_layer(id)?;
Ok(())
}

/// Mutate the document by applying the `operation` to it. If the operation necessitates a
/// reaction from the frontend, responses may be returned.
pub fn handle_operation(&mut self, operation: Operation) -> Result<Option<Vec<DocumentResponse>>, DocumentError> {
let responses = match &operation {
Operation::AddCircle { path, insert_index, cx, cy, r, style } => {
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Circle(layers::Circle::new((*cx, *cy), *r, *style))), *insert_index)?;
Operation::AddEllipse { path, insert_index, transform, style } => {
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Ellipse(layers::Ellipse::new()), *transform, *style), *insert_index)?;
let path = [path.clone(), vec![id]].concat();

Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
}
Operation::AddEllipse {
path,
insert_index,
cx,
cy,
rx,
ry,
rot,
style,
} => {
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Ellipse(layers::Ellipse::new((*cx, *cy), (*rx, *ry), *rot, *style))), *insert_index)?;
Operation::AddRect { path, insert_index, transform, style } => {
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Rect(Rect::new()), *transform, *style), *insert_index)?;
let path = [path.clone(), vec![id]].concat();

Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
}
Operation::AddRect {
path,
insert_index,
x0,
y0,
x1,
y1,
style,
} => {
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Rect(Rect::new((*x0, *y0), (*x1, *y1), *style))), *insert_index)?;
Operation::AddLine { path, insert_index, transform, style } => {
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Line(Line::new()), *transform, *style), *insert_index)?;
let path = [path.clone(), vec![id]].concat();

Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
}
Operation::AddLine {
Operation::AddPen {
path,
insert_index,
x0,
y0,
x1,
y1,
points,
transform,
style,
} => {
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Line(Line::new((*x0, *y0), (*x1, *y1), *style))), *insert_index)?;
let path = [path.clone(), vec![id]].concat();

Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
}
Operation::AddPen { path, insert_index, points, style } => {
let points: Vec<kurbo::Point> = points.iter().map(|&it| it.into()).collect();
let polyline = PolyLine::new(points, *style);
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline)), *insert_index)?;
let points: Vec<glam::DVec2> = points.iter().map(|&it| it.into()).collect();
let polyline = PolyLine::new(points);
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline), *transform, *style), *insert_index)?;
Some(vec![DocumentResponse::DocumentChanged])
}
Operation::AddShape {
path,
insert_index,
x0,
y0,
x1,
y1,
transform,
equal_sides,
sides,
style,
} => {
let s = Shape::new((*x0, *y0), (*x1, *y1), *sides, *style);
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Shape(s)), *insert_index)?;
let s = Shape::new(*equal_sides, *sides);
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Shape(s), *transform, *style), *insert_index)?;
let path = [path.clone(), vec![id]].concat();

Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
Expand All @@ -256,27 +234,34 @@ impl Document {
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::FolderChanged { path: folder_path.to_vec() }])
}
Operation::AddFolder { path } => {
self.set_layer(&path, Layer::new(LayerDataTypes::Folder(Folder::default())))?;
self.set_layer(&path, Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default()))?;

Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::FolderChanged { path: path.clone() }])
}
Operation::MountWorkingFolder { path } => {
self.work_mount_path = path.clone();
self.work_operations.clear();
self.work = Folder::default();
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
self.work_mounted = true;
None
}
Operation::TransformLayer { path, transform } => {
let transform = self.root.transform * DAffine2::from_cols_array(&transform);
let layer = self.document_folder_mut(path).unwrap();
layer.transform = transform;
layer.cache_dirty = true;
Some(vec![DocumentResponse::DocumentChanged])
}
Operation::DiscardWorkingFolder => {
self.work_operations.clear();
self.work_mount_path = vec![];
self.work = Folder::default();
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
self.work_mounted = false;
Some(vec![DocumentResponse::DocumentChanged])
}
Operation::ClearWorkingFolder => {
self.work_operations.clear();
self.work = Folder::default();
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
Some(vec![DocumentResponse::DocumentChanged])
}
Operation::CommitTransaction => {
Expand All @@ -286,7 +271,7 @@ impl Document {
std::mem::swap(&mut ops, &mut self.work_operations);
self.work_mounted = false;
self.work_mount_path = vec![];
self.work = Folder::default();
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
let mut responses = vec![];
for operation in ops.into_iter() {
if let Some(mut op_responses) = self.handle_operation(operation)? {
Expand Down
32 changes: 0 additions & 32 deletions core/document/src/layers/circle.rs

This file was deleted.

33 changes: 10 additions & 23 deletions core/document/src/layers/ellipse.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
use kurbo::Shape;

use super::style;
use super::LayerData;

use std::fmt::Write;

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Ellipse {
shape: kurbo::Ellipse,
style: style::PathStyle,
}
pub struct Ellipse {}

impl Ellipse {
pub fn new(center: impl Into<kurbo::Point>, radii: impl Into<kurbo::Vec2>, rotation: f64, style: style::PathStyle) -> Ellipse {
Ellipse {
shape: kurbo::Ellipse::new(center, radii, rotation),
style,
}
pub fn new() -> Ellipse {
Ellipse {}
}
}

impl LayerData for Ellipse {
fn render(&mut self, svg: &mut String) {
let kurbo::Vec2 { x: rx, y: ry } = self.shape.radii();
let kurbo::Point { x: cx, y: cy } = self.shape.center();

let _ = write!(
svg,
r#"<ellipse cx="0" cy="0" rx="{}" ry="{}" transform="translate({} {}) rotate({})"{} />"#,
rx,
ry,
cx,
cy,
self.shape.rotation().to_degrees(),
self.style.render(),
);
fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
kurbo::Ellipse::from_affine(kurbo::Affine::new(transform.to_cols_array())).to_path(0.1)
}
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
let _ = write!(svg, r#"<path d="{}" {} />"#, self.to_kurbo_path(transform, style).to_svg(), style.render());
}
}
Loading