Skip to content

Commit f76517a

Browse files
0HyperCubeakshay1992kalbhorKeavon
authored
Add a movable canvas with matricies (#175)
* Convert polygon and rectangle tool to kurbo::BezPath * Add glam * Add affine transform to elipse and remove circle * Format * Add svg group and add matrix for group * Convert all operations to use matricies * Work uses same transform as root * Format * Frontend fixed to render changes to working colors when changed from backend (#180) * Backend and Frontend modification to show working color mods * Remove comments & change precedence for tool and doc actions * Add keybind for resetting work colors * Minor Frontend changes * Remove early sample "greet" code * Add a contributing section to the project README * Add moving document around * Add document transform for tools * Update to GraphiteEditor's fork * Use write in foreach for rendering group / folder * Add missing TranslateDown action * Use points for line operation * Format * Add todo to change to shape's aspect ratio * Remove empty if * Initial pass at refactor * Fix polyline test * Use document message to modify document transform * Messages -> Operations * Transform layer * Format * Use DAffine2::IDENTITY * Clean up kurbo generation for line and rect * Use .into for rectangle points * Rename cols to transform * Rename other cols to transform * Add todo for into_iter * Remove unnecessary clone Co-authored-by: akshay1992kalbhor <[email protected]> Co-authored-by: Keavon Chambers <[email protected]>
1 parent 60cf084 commit f76517a

25 files changed

+394
-445
lines changed

Cargo.lock

Lines changed: 9 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/document/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@ license = "Apache-2.0"
1010

1111
[dependencies]
1212
log = "0.4"
13-
kurbo = "0.8.0"
13+
# TODO: Swich to kurbo release when derive `PartialEq` is available for BezPath
14+
#kurbo = "0.8.0"
15+
kurbo = {git="https://github.com/GraphiteEditor/kurbo.git", branch="bezpath-partial-eq"}
1416
serde = { version = "1.0", features = ["derive"] }
17+
glam = "0.16"

core/document/src/document.rs

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
use glam::DAffine2;
2+
13
use crate::{
2-
layers::{self, Folder, Layer, LayerData, LayerDataTypes, Line, PolyLine, Rect, Shape},
4+
layers::{self, style::PathStyle, Folder, Layer, LayerDataTypes, Line, PolyLine, Rect, Shape},
35
DocumentError, DocumentResponse, LayerId, Operation,
46
};
57

68
#[derive(Debug, Clone, PartialEq)]
79
pub struct Document {
8-
pub root: layers::Folder,
9-
pub work: Folder,
10+
pub root: Layer,
11+
pub work: Layer,
1012
pub work_mount_path: Vec<LayerId>,
1113
pub work_operations: Vec<Operation>,
1214
pub work_mounted: bool,
@@ -15,8 +17,8 @@ pub struct Document {
1517
impl Default for Document {
1618
fn default() -> Self {
1719
Self {
18-
root: Folder::default(),
19-
work: Folder::default(),
20+
root: Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default()),
21+
work: Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default()),
2022
work_mount_path: Vec::new(),
2123
work_operations: Vec::new(),
2224
work_mounted: false,
@@ -41,8 +43,11 @@ impl Document {
4143
return;
4244
}
4345
if path.as_slice() == self.work_mount_path {
44-
self.document_folder_mut(path).unwrap().render(svg);
45-
self.work.render(svg);
46+
// TODO: Handle if mounted in nested folders
47+
let transform = self.document_folder(path).unwrap().transform;
48+
self.document_folder_mut(path).unwrap().render_as_folder(svg);
49+
self.work.transform = transform;
50+
self.work.render_as_folder(svg);
4651
path.pop();
4752
}
4853
let ids = self.folder(path).unwrap().layer_ids.clone();
@@ -68,10 +73,10 @@ impl Document {
6873
/// This function respects mounted folders and will thus not contain the layers already
6974
/// present in the document if a temporary folder is mounted on top.
7075
pub fn folder(&self, mut path: &[LayerId]) -> Result<&Folder, DocumentError> {
71-
let mut root = &self.root;
76+
let mut root = self.root.as_folder()?;
7277
if self.is_mounted(self.work_mount_path.as_slice(), path) {
7378
path = &path[self.work_mount_path.len()..];
74-
root = &self.work;
79+
root = self.work.as_folder()?;
7580
}
7681
for id in path {
7782
root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?;
@@ -87,9 +92,9 @@ impl Document {
8792
pub fn folder_mut(&mut self, mut path: &[LayerId]) -> Result<&mut Folder, DocumentError> {
8893
let mut root = if self.is_mounted(self.work_mount_path.as_slice(), path) {
8994
path = &path[self.work_mount_path.len()..];
90-
&mut self.work
95+
self.work.as_folder_mut()?
9196
} else {
92-
&mut self.root
97+
self.root.as_folder_mut()?
9398
};
9499
for id in path {
95100
root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?;
@@ -101,10 +106,10 @@ impl Document {
101106
/// or if the requested layer is not of type folder.
102107
/// This function does **not** respect mounted folders and will always return the current
103108
/// state of the document, disregarding any temporary modifications.
104-
pub fn document_folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> {
109+
pub fn document_folder(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> {
105110
let mut root = &self.root;
106111
for id in path {
107-
root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?;
112+
root = root.as_folder()?.layer(*id).ok_or(DocumentError::LayerNotFound)?;
108113
}
109114
Ok(root)
110115
}
@@ -114,10 +119,10 @@ impl Document {
114119
/// This function does **not** respect mounted folders and will always return the current
115120
/// state of the document, disregarding any temporary modifications.
116121
/// If you manually edit the folder you have to set the cache_dirty flag yourself.
117-
pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> {
122+
pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Layer, DocumentError> {
118123
let mut root = &mut self.root;
119124
for id in path {
120-
root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?;
125+
root = root.as_folder_mut()?.layer_mut(*id).ok_or(DocumentError::LayerNotFound)?;
121126
}
122127
Ok(root)
123128
}
@@ -137,7 +142,7 @@ impl Document {
137142

138143
/// Replaces the layer at the specified `path` with `layer`.
139144
pub fn set_layer(&mut self, path: &[LayerId], layer: Layer) -> Result<(), DocumentError> {
140-
let mut folder = &mut self.root;
145+
let mut folder = self.root.as_folder_mut()?;
141146
if let Ok((path, id)) = split_path(path) {
142147
self.layer_mut(path)?.cache_dirty = true;
143148
folder = self.folder_mut(path)?;
@@ -163,81 +168,54 @@ impl Document {
163168
pub fn delete(&mut self, path: &[LayerId]) -> Result<(), DocumentError> {
164169
let (path, id) = split_path(path)?;
165170
let _ = self.layer_mut(path).map(|x| x.cache_dirty = true);
166-
self.document_folder_mut(path)?.remove_layer(id)?;
171+
self.document_folder_mut(path)?.as_folder_mut()?.remove_layer(id)?;
167172
Ok(())
168173
}
169174

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

178183
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
179184
}
180-
Operation::AddEllipse {
181-
path,
182-
insert_index,
183-
cx,
184-
cy,
185-
rx,
186-
ry,
187-
rot,
188-
style,
189-
} => {
190-
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Ellipse(layers::Ellipse::new((*cx, *cy), (*rx, *ry), *rot, *style))), *insert_index)?;
185+
Operation::AddRect { path, insert_index, transform, style } => {
186+
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Rect(Rect::new()), *transform, *style), *insert_index)?;
191187
let path = [path.clone(), vec![id]].concat();
192188

193189
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
194190
}
195-
Operation::AddRect {
196-
path,
197-
insert_index,
198-
x0,
199-
y0,
200-
x1,
201-
y1,
202-
style,
203-
} => {
204-
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Rect(Rect::new((*x0, *y0), (*x1, *y1), *style))), *insert_index)?;
191+
Operation::AddLine { path, insert_index, transform, style } => {
192+
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Line(Line::new()), *transform, *style), *insert_index)?;
205193
let path = [path.clone(), vec![id]].concat();
206194

207195
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
208196
}
209-
Operation::AddLine {
197+
Operation::AddPen {
210198
path,
211199
insert_index,
212-
x0,
213-
y0,
214-
x1,
215-
y1,
200+
points,
201+
transform,
216202
style,
217203
} => {
218-
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Line(Line::new((*x0, *y0), (*x1, *y1), *style))), *insert_index)?;
219-
let path = [path.clone(), vec![id]].concat();
220-
221-
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::SelectLayer { path }])
222-
}
223-
Operation::AddPen { path, insert_index, points, style } => {
224-
let points: Vec<kurbo::Point> = points.iter().map(|&it| it.into()).collect();
225-
let polyline = PolyLine::new(points, *style);
226-
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline)), *insert_index)?;
204+
let points: Vec<glam::DVec2> = points.iter().map(|&it| it.into()).collect();
205+
let polyline = PolyLine::new(points);
206+
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline), *transform, *style), *insert_index)?;
227207
Some(vec![DocumentResponse::DocumentChanged])
228208
}
229209
Operation::AddShape {
230210
path,
231211
insert_index,
232-
x0,
233-
y0,
234-
x1,
235-
y1,
212+
transform,
213+
equal_sides,
236214
sides,
237215
style,
238216
} => {
239-
let s = Shape::new((*x0, *y0), (*x1, *y1), *sides, *style);
240-
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Shape(s)), *insert_index)?;
217+
let s = Shape::new(*equal_sides, *sides);
218+
let id = self.add_layer(&path, Layer::new(LayerDataTypes::Shape(s), *transform, *style), *insert_index)?;
241219
let path = [path.clone(), vec![id]].concat();
242220

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

261239
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::FolderChanged { path: path.clone() }])
262240
}
263241
Operation::MountWorkingFolder { path } => {
264242
self.work_mount_path = path.clone();
265243
self.work_operations.clear();
266-
self.work = Folder::default();
244+
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
267245
self.work_mounted = true;
268246
None
269247
}
248+
Operation::TransformLayer { path, transform } => {
249+
let transform = self.root.transform * DAffine2::from_cols_array(&transform);
250+
let layer = self.document_folder_mut(path).unwrap();
251+
layer.transform = transform;
252+
layer.cache_dirty = true;
253+
Some(vec![DocumentResponse::DocumentChanged])
254+
}
270255
Operation::DiscardWorkingFolder => {
271256
self.work_operations.clear();
272257
self.work_mount_path = vec![];
273-
self.work = Folder::default();
258+
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
274259
self.work_mounted = false;
275260
Some(vec![DocumentResponse::DocumentChanged])
276261
}
277262
Operation::ClearWorkingFolder => {
278263
self.work_operations.clear();
279-
self.work = Folder::default();
264+
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
280265
Some(vec![DocumentResponse::DocumentChanged])
281266
}
282267
Operation::CommitTransaction => {
@@ -286,7 +271,7 @@ impl Document {
286271
std::mem::swap(&mut ops, &mut self.work_operations);
287272
self.work_mounted = false;
288273
self.work_mount_path = vec![];
289-
self.work = Folder::default();
274+
self.work = Layer::new(LayerDataTypes::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array(), PathStyle::default());
290275
let mut responses = vec![];
291276
for operation in ops.into_iter() {
292277
if let Some(mut op_responses) = self.handle_operation(operation)? {

core/document/src/layers/circle.rs

Lines changed: 0 additions & 32 deletions
This file was deleted.

core/document/src/layers/ellipse.rs

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,24 @@
1+
use kurbo::Shape;
2+
13
use super::style;
24
use super::LayerData;
35

46
use std::fmt::Write;
57

68
#[derive(Debug, Clone, Copy, PartialEq, Default)]
7-
pub struct Ellipse {
8-
shape: kurbo::Ellipse,
9-
style: style::PathStyle,
10-
}
9+
pub struct Ellipse {}
1110

1211
impl Ellipse {
13-
pub fn new(center: impl Into<kurbo::Point>, radii: impl Into<kurbo::Vec2>, rotation: f64, style: style::PathStyle) -> Ellipse {
14-
Ellipse {
15-
shape: kurbo::Ellipse::new(center, radii, rotation),
16-
style,
17-
}
12+
pub fn new() -> Ellipse {
13+
Ellipse {}
1814
}
1915
}
2016

2117
impl LayerData for Ellipse {
22-
fn render(&mut self, svg: &mut String) {
23-
let kurbo::Vec2 { x: rx, y: ry } = self.shape.radii();
24-
let kurbo::Point { x: cx, y: cy } = self.shape.center();
25-
26-
let _ = write!(
27-
svg,
28-
r#"<ellipse cx="0" cy="0" rx="{}" ry="{}" transform="translate({} {}) rotate({})"{} />"#,
29-
rx,
30-
ry,
31-
cx,
32-
cy,
33-
self.shape.rotation().to_degrees(),
34-
self.style.render(),
35-
);
18+
fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
19+
kurbo::Ellipse::from_affine(kurbo::Affine::new(transform.to_cols_array())).to_path(0.1)
20+
}
21+
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
22+
let _ = write!(svg, r#"<path d="{}" {} />"#, self.to_kurbo_path(transform, style).to_svg(), style.render());
3623
}
3724
}

0 commit comments

Comments
 (0)