mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 03:38:11 +08:00
Implement anchor and handle point rendering with the Path Tool (#353)
* Implement Path Tool * Draw a red rectangle where the first point on the shape is * Correctly render anchors, handles, and connecting lines * Fix drain() which can panic * Refactor frontend messages to work as return values not callbacks * Reduce the number of unnecessary frontend updates * Fix stack overflow by using a loop * Group Document Render calls and put them at the end * Speed hacks for dirtification * Add performance * Bunch folder changed updates * Add triggers to redraw overlays to movement_handler * Polish the pixel-perfect rendering of vector manipulators * Restore scrollbars that were disabled * Cleanup * WIP Add shape outline rendering * Fix compiling * Add outlines to selected shapes * Fix outlines rendering over handles and anchors * Fix dirtification * Add a comment * Address code review feedback * Formatting * Small tweaks Co-authored-by: Oliver Davies <oliver@psyfer.io> Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
co-authored by
Oliver Davies
Dennis Kobert
parent
8336b3e809
commit
913b9365e2
+89
-13
@@ -215,7 +215,6 @@ impl Document {
|
||||
}
|
||||
|
||||
pub fn mark_as_dirty(&mut self, path: &[LayerId]) -> Result<(), DocumentError> {
|
||||
self.mark_downstream_as_dirty(path)?;
|
||||
self.mark_upstream_as_dirty(path)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -264,6 +263,10 @@ impl Document {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_transform_relative_to_viewport(&self, from: &[LayerId]) -> Result<DAffine2, DocumentError> {
|
||||
self.generate_transform_across_scope(from, None)
|
||||
}
|
||||
|
||||
pub fn apply_transform_relative_to_viewport(&mut self, layer: &[LayerId], transform: DAffine2) -> Result<(), DocumentError> {
|
||||
self.transform_relative_to_scope(layer, None, transform)
|
||||
}
|
||||
@@ -297,33 +300,79 @@ impl Document {
|
||||
|
||||
let responses = match &operation {
|
||||
Operation::AddEllipse { path, insert_index, transform, style } => {
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(Shape::ellipse(*style)), *transform), *insert_index)?;
|
||||
let layer = Layer::new(LayerDataType::Shape(Shape::ellipse(*style)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddOverlayEllipse { path, transform, style } => {
|
||||
let mut ellipse = Shape::ellipse(*style);
|
||||
ellipse.render_index = -1;
|
||||
|
||||
let mut layer = Layer::new(LayerDataType::Shape(ellipse), *transform);
|
||||
layer.overlay = true;
|
||||
|
||||
self.set_layer(path, layer, -1)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddRect { path, insert_index, transform, style } => {
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(Shape::rectangle(*style)), *transform), *insert_index)?;
|
||||
let layer = Layer::new(LayerDataType::Shape(Shape::rectangle(*style)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddBoundingBox { path, transform, style } => {
|
||||
Operation::AddOverlayRect { path, transform, style } => {
|
||||
let mut rect = Shape::rectangle(*style);
|
||||
rect.render_index = -1;
|
||||
|
||||
let mut layer = Layer::new(LayerDataType::Shape(rect), *transform);
|
||||
layer.overlay = true;
|
||||
|
||||
self.set_layer(path, layer, -1)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddShape {
|
||||
Operation::AddLine { path, insert_index, transform, style } => {
|
||||
let layer = Layer::new(LayerDataType::Shape(Shape::line(*style)), *transform);
|
||||
|
||||
self.set_layer(path, layer, *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddOverlayLine { path, transform, style } => {
|
||||
let mut line = Shape::line(*style);
|
||||
line.render_index = -1;
|
||||
|
||||
let mut layer = Layer::new(LayerDataType::Shape(line), *transform);
|
||||
layer.overlay = true;
|
||||
|
||||
self.set_layer(path, layer, -1)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddNgon {
|
||||
path,
|
||||
insert_index,
|
||||
transform,
|
||||
style,
|
||||
sides,
|
||||
} => {
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(Shape::shape(*sides, *style)), *transform), *insert_index)?;
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(Shape::ngon(*sides, *style)), *transform), *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddLine { path, insert_index, transform, style } => {
|
||||
self.set_layer(path, Layer::new(LayerDataType::Shape(Shape::line(*style)), *transform), *insert_index)?;
|
||||
Operation::AddOverlayShape { path, style, bez_path } => {
|
||||
let mut shape = Shape::from_bez_path(bez_path.clone(), *style, false);
|
||||
shape.render_index = -1;
|
||||
|
||||
let mut layer = Layer::new(LayerDataType::Shape(shape), DAffine2::IDENTITY.to_cols_array());
|
||||
layer.overlay = true;
|
||||
|
||||
self.set_layer(path, layer, -1)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::CreatedLayer { path: path.clone() }])
|
||||
}
|
||||
Operation::AddPen {
|
||||
@@ -395,6 +444,19 @@ impl Document {
|
||||
self.mark_as_dirty(path)?;
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::SetShapePathInViewport { path, bez_path, transform } => {
|
||||
let transform = DAffine2::from_cols_array(transform);
|
||||
self.set_transform_relative_to_viewport(path, transform)?;
|
||||
self.mark_as_dirty(path)?;
|
||||
|
||||
match &mut self.layer_mut(path)?.data {
|
||||
LayerDataType::Shape(shape) => {
|
||||
shape.path = bez_path.clone();
|
||||
}
|
||||
LayerDataType::Folder(_) => (),
|
||||
}
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::TransformLayerInScope { path, transform, scope } => {
|
||||
let transform = DAffine2::from_cols_array(transform);
|
||||
let scope = DAffine2::from_cols_array(scope);
|
||||
@@ -416,11 +478,16 @@ impl Document {
|
||||
self.mark_as_dirty(path)?;
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::ToggleVisibility { path } => {
|
||||
Operation::ToggleLayerVisibility { path } => {
|
||||
self.mark_as_dirty(path)?;
|
||||
if let Ok(layer) = self.layer_mut(path) {
|
||||
layer.visible = !layer.visible;
|
||||
}
|
||||
let layer = self.layer_mut(path)?;
|
||||
layer.visible = !layer.visible;
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::SetLayerVisibility { path, visible } => {
|
||||
self.mark_as_dirty(path)?;
|
||||
let layer = self.layer_mut(path)?;
|
||||
layer.visible = *visible;
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::SetLayerBlendMode { path, blend_mode } => {
|
||||
@@ -435,7 +502,16 @@ impl Document {
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::FillLayer { path, color } => {
|
||||
Operation::SetLayerStyle { path, style } => {
|
||||
let layer = self.layer_mut(path)?;
|
||||
match &mut layer.data {
|
||||
LayerDataType::Shape(s) => s.style = *style,
|
||||
_ => return Err(DocumentError::NotAShape),
|
||||
}
|
||||
self.mark_as_dirty(path)?;
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::LayerChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::SetLayerFill { path, color } => {
|
||||
let layer = self.layer_mut(path)?;
|
||||
match &mut layer.data {
|
||||
LayerDataType::Shape(s) => s.style.set_fill(layers::style::Fill::new(*color)),
|
||||
|
||||
Reference in New Issue
Block a user