Files
Graphite/editor/src/document/document_message.rs
caleb f9f66db5a2 Boolean shape operations (#470)
* added BooleanUnion message for select tool, which is sent by the BooleanUnion button

* select to dispatches a BooleanUnion message
   - as far as I can tell the selected layers aren't stored anywhere

* Added neccesary messages and functions for boolean operation

* Intersection code, as yet untested
does not compile

* Updated intersection algorithm

* Fixed shapes_as_seen
   - should not be effected by root transform, should be effected by the shape transform
Working line intercepts

* updated intersection algorithm

* added SubCurve struct, to reduce creation of new PathSegs and more efficiently calculate bounding boxes

* intersection algorithm modifications

* changed sub_curve to do less copying

* intersection algorithm working for ellipses
   - idk why though, the algorithm isn't finding close, but nearby intersections

* Code for the sub-shape identification pieces of boolean operation algorithm

* cycle direction calculations
boolean_operation code

* final touches (a.k.a. the very begining touches before the debugging)

* removed intersection testing code to draw intersects
added code to add new shapes to document

* Bug Fix: Cycle needs to track egde origin
Bug Fix: vertex markers
Working for simple shapes

* Bug Fix: multiple intersections in same PathSeg

* Bug Fix: compare the absolute value of area magnitudes

* Bug Fix: subdivision algorithm

* cargo fmt

* comments

* Bug Fix: Difference and Subtraction operations

* simplified overlap

* changed shapes_as_seen to transformed_shapes
    - modified function to use existing code for multiplying transforms and traversing layer tree

removed shape_as_seen, helper function to shapes_as_seen

* Bug Fix: selected layers must be sorted

* Changed SubFront to SubtractFront and SubBack to SubtractBack

* moved F64PRECISION to graphene::consts

* Best practices changes
intersection algorithm changes

* Added BooleanOperationError

* modified intersection algorithm to use more dynamic thresholds

* intersection algorithm modification

* Added "do_if"

* Bug Fix: properly subdivide segments with multiple intersections

* Added tests for intersection algorithm

* restructured flow control in intersection algorithm
restructured intersection algorithm to modify single vector
    - should have done this yeeeaars ago

* Shapes will have the pathstyle of alpha

* collect_shapes now uses closure

* BugFix: fixed PathGraph:get_cycles, prevent multicounting cycles

* Added boolean_ops::reverse_path function

* Curves are now reversed before intersections are found

* cleanup

* BugFix: subfront and subback chose wrong shape pathstyle
BugFix: path concat should remove internal movetos and closepaths

* BugFix: prevent movetos from being added due to intersect imprecision

* Changed intersection quality threshhold to CURVE_FIDELITY

* Added functions for cubic/quadratic roots
!Does not compile!

* Added special case algorithms for line-curve intersections
Added tests for cubic root algorithm

* changed line_curve_intersections structure

* Handle intersecting shape without curve intersection case

* Behavior for SubtratFront, SubtractBack, Union, and Intersection for the no-intersect cases

* reformatted PathGraph::add_edges_from_path
Fixed bugs involving closepaths and subpaths

* Bug fix: fix refactoring error

* Bug fix: don't consider 0 length cycles
Bug Fix: dummy vertices are not intersections

* the function document_message_handler::sort_layers sorts the layers from bottom to top
    - we want the reverse

* Bug Fix: ClosePath must be appended to shapes constructed from cycle
    - By default PathGraph edges are not closed.
    - When the input shape was not closed there would be multiple unclosed paths in the resulting shape, causing bugs

* close_path now closes all subpaths in a path
    - if a shape has any subpath which is not closed boolean operations are undefined
        (Also PathGraph::add_edges_from_path breaks when the path isn't closed)

* clean up

* removed unused function "intersectoin_candidates"

* comments

* removed duplicate check for valid intersection in horizontal_ray_cast

* Temporary fix for ray casting

* it wouldn't build cause one of these ->' pesky things was hanging around in the wrong neighborhood

* ignoring intersection test cases because they do exact fp comparison

* Some spelling fixes and abbreviation burnination (more needed)

* spelling fixes
close_path bug

* Fixed: local extrema were not being properly filtered out for subcurves
Fixed: reversing PathSeg removes any closepaths

* spelling

* Code review pass

* partial implementation overlapping identical curves

* Untested implementation for test for overlapping curves
    - Test is not yet used in intersection algorithm

* -Removed PathGraph::intersect()

* readability improvements
changed line_t_value to be more forgiving to error

* Bug Fix: match_control_polygon didn't properly compare different degree polygons

* Added colinear() function
Added projection_on_line() function

* BugFix project_onto_line

* removed extra log

* rust fmt

Co-authored-by: Keavon Chambers <keavon@keavon.com>
Co-authored-by: TrueDoctor <dennis@kobert.dev>
2022-02-12 09:30:08 -08:00

160 lines
3.3 KiB
Rust

use super::layer_panel::LayerMetadata;
use super::utility_types::{AlignAggregate, AlignAxis, FlipAxis};
use crate::message_prelude::*;
use graphene::boolean_ops::BooleanOperation as BooleanOperationType;
use graphene::layers::blend_mode::BlendMode;
use graphene::layers::style::ViewMode;
use graphene::LayerId;
use graphene::Operation as DocumentOperation;
use serde::{Deserialize, Serialize};
#[remain::sorted]
#[impl_message(Message, PortfolioMessage, Document)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum DocumentMessage {
// Sub-messages
#[remain::unsorted]
DispatchOperation(Box<DocumentOperation>),
#[remain::unsorted]
#[child]
Artboard(ArtboardMessage),
#[remain::unsorted]
#[child]
Movement(MovementMessage),
#[remain::unsorted]
#[child]
Overlays(OverlaysMessage),
#[remain::unsorted]
#[child]
TransformLayers(TransformLayerMessage),
#[remain::unsorted]
#[child]
PropertiesPanel(PropertiesPanelMessage),
// Messages
AbortTransaction,
AddSelectedLayers {
additional_layers: Vec<Vec<LayerId>>,
},
AlignSelectedLayers {
axis: AlignAxis,
aggregate: AlignAggregate,
},
BooleanOperation(BooleanOperationType),
CommitTransaction,
CreateEmptyFolder {
container_path: Vec<LayerId>,
},
DebugPrintDocument,
DeleteLayer {
layer_path: Vec<LayerId>,
},
DeleteSelectedLayers,
DeselectAllLayers,
DirtyRenderDocument,
DirtyRenderDocumentInOutlineView,
DocumentHistoryBackward,
DocumentHistoryForward,
DocumentStructureChanged,
DuplicateSelectedLayers,
ExportDocument,
FlipSelectedLayers {
flip_axis: FlipAxis,
},
FolderChanged {
affected_folder_path: Vec<LayerId>,
},
GroupSelectedLayers,
LayerChanged {
affected_layer_path: Vec<LayerId>,
},
MoveSelectedLayersTo {
folder_path: Vec<LayerId>,
insert_index: isize,
reverse_index: bool,
},
NudgeSelectedLayers {
delta_x: f64,
delta_y: f64,
},
Redo,
RenameLayer {
layer_path: Vec<LayerId>,
new_name: String,
},
RenderDocument,
ReorderSelectedLayers {
relative_index_offset: isize,
},
RollbackTransaction,
SaveDocument,
SelectAllLayers,
SelectionChanged,
SelectLayer {
layer_path: Vec<LayerId>,
ctrl: bool,
shift: bool,
},
SetBlendModeForSelectedLayers {
blend_mode: BlendMode,
},
SetLayerExpansion {
layer_path: Vec<LayerId>,
set_expanded: bool,
},
SetLayerName {
layer_path: Vec<LayerId>,
name: String,
},
SetOpacityForSelectedLayers {
opacity: f64,
},
SetOverlaysVisibility {
visible: bool,
},
SetSelectedLayers {
replacement_selected_layers: Vec<Vec<LayerId>>,
},
SetSnapping {
snap: bool,
},
SetTexboxEditability {
path: Vec<LayerId>,
editable: bool,
},
SetViewMode {
view_mode: ViewMode,
},
StartTransaction,
ToggleLayerExpansion {
layer_path: Vec<LayerId>,
},
ToggleLayerVisibility {
layer_path: Vec<LayerId>,
},
Undo,
UngroupLayers {
folder_path: Vec<LayerId>,
},
UngroupSelectedLayers,
UpdateLayerMetadata {
layer_path: Vec<LayerId>,
layer_metadata: LayerMetadata,
},
ZoomCanvasToFitAll,
}
impl From<DocumentOperation> for DocumentMessage {
fn from(operation: DocumentOperation) -> DocumentMessage {
DocumentMessage::DispatchOperation(Box::new(operation))
}
}
impl From<DocumentOperation> for Message {
fn from(operation: DocumentOperation) -> Message {
DocumentMessage::DispatchOperation(Box::new(operation)).into()
}
}