11use crate :: instances:: Instance ;
2- use crate :: math:: math_ext:: QuadExt ;
32pub use crate :: math:: quad:: Quad ;
43pub use crate :: math:: rect:: Rect ;
54use crate :: raster:: { BlendMode , Image } ;
65use crate :: raster_types:: { CPU , GPU , RasterDataTable } ;
76use crate :: transform:: { Footprint , Transform } ;
87use crate :: uuid:: { NodeId , generate_uuid} ;
8+ use crate :: vector:: VectorDataTable ;
9+ use crate :: vector:: click_target:: { ClickTarget , FreePoint } ;
910use crate :: vector:: style:: { Fill , Stroke , StrokeAlign , ViewMode } ;
10- use crate :: vector:: { PointId , VectorDataTable } ;
1111use crate :: { Artboard , ArtboardGroupTable , Color , GraphicElement , GraphicGroupTable } ;
1212use bezier_rs:: Subpath ;
1313use dyn_any:: DynAny ;
14- use glam:: { DAffine2 , DMat2 , DVec2 } ;
14+ use glam:: { DAffine2 , DVec2 } ;
1515use num_traits:: Zero ;
1616use std:: collections:: { HashMap , HashSet } ;
1717use std:: fmt:: Write ;
1818#[ cfg( feature = "vello" ) ]
1919use vello:: * ;
2020
21- #[ derive( Copy , Clone , Debug , PartialEq , serde:: Serialize , serde:: Deserialize ) ]
22- pub struct FreePoint {
23- pub id : PointId ,
24- pub position : DVec2 ,
25- }
26-
27- impl FreePoint {
28- pub fn new ( id : PointId , position : DVec2 ) -> Self {
29- Self { id, position }
30- }
31-
32- pub fn apply_transform ( & mut self , transform : DAffine2 ) {
33- self . position = transform. transform_point2 ( self . position ) ;
34- }
35- }
36-
37- #[ derive( Clone , Debug , PartialEq , serde:: Serialize , serde:: Deserialize ) ]
38- pub enum ClickTargetType {
39- Subpath ( Subpath < PointId > ) ,
40- FreePoint ( FreePoint ) ,
41- }
42-
43- /// Represents a clickable target for the layer
44- #[ derive( Clone , Debug , PartialEq , serde:: Serialize , serde:: Deserialize ) ]
45- pub struct ClickTarget {
46- target_type : ClickTargetType ,
47- stroke_width : f64 ,
48- bounding_box : Option < [ DVec2 ; 2 ] > ,
49- }
50-
5121#[ derive( Clone , Copy , Debug , PartialEq , serde:: Serialize , serde:: Deserialize ) ]
5222enum MaskType {
5323 Clip ,
@@ -71,133 +41,6 @@ impl MaskType {
7141 }
7242}
7343
74- impl ClickTarget {
75- pub fn new_with_subpath ( subpath : Subpath < PointId > , stroke_width : f64 ) -> Self {
76- let bounding_box = subpath. loose_bounding_box ( ) ;
77- Self {
78- target_type : ClickTargetType :: Subpath ( subpath) ,
79- stroke_width,
80- bounding_box,
81- }
82- }
83-
84- pub fn new_with_free_point ( point : FreePoint ) -> Self {
85- const MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT : f64 = 1e-4 / 2. ;
86- let stroke_width = 10. ;
87- let bounding_box = Some ( [
88- point. position - DVec2 :: splat ( MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT ) ,
89- point. position + DVec2 :: splat ( MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT ) ,
90- ] ) ;
91-
92- Self {
93- target_type : ClickTargetType :: FreePoint ( point) ,
94- stroke_width,
95- bounding_box,
96- }
97- }
98-
99- pub fn target_type ( & self ) -> & ClickTargetType {
100- & self . target_type
101- }
102-
103- pub fn bounding_box ( & self ) -> Option < [ DVec2 ; 2 ] > {
104- self . bounding_box
105- }
106-
107- pub fn bounding_box_with_transform ( & self , transform : DAffine2 ) -> Option < [ DVec2 ; 2 ] > {
108- self . bounding_box . map ( |[ a, b] | [ transform. transform_point2 ( a) , transform. transform_point2 ( b) ] )
109- }
110-
111- pub fn apply_transform ( & mut self , affine_transform : DAffine2 ) {
112- match self . target_type {
113- ClickTargetType :: Subpath ( ref mut subpath) => {
114- subpath. apply_transform ( affine_transform) ;
115- }
116- ClickTargetType :: FreePoint ( ref mut point) => {
117- point. apply_transform ( affine_transform) ;
118- }
119- }
120- self . update_bbox ( ) ;
121- }
122-
123- fn update_bbox ( & mut self ) {
124- match self . target_type {
125- ClickTargetType :: Subpath ( ref subpath) => {
126- self . bounding_box = subpath. bounding_box ( ) ;
127- }
128- ClickTargetType :: FreePoint ( ref point) => {
129- self . bounding_box = Some ( [ point. position - DVec2 :: splat ( self . stroke_width / 2. ) , point. position + DVec2 :: splat ( self . stroke_width / 2. ) ] ) ;
130- }
131- }
132- }
133-
134- /// Does the click target intersect the path
135- pub fn intersect_path < It : Iterator < Item = bezier_rs:: Bezier > > ( & self , mut bezier_iter : impl FnMut ( ) -> It , layer_transform : DAffine2 ) -> bool {
136- // Check if the matrix is not invertible
137- let mut layer_transform = layer_transform;
138- if layer_transform. matrix2 . determinant ( ) . abs ( ) <= f64:: EPSILON {
139- layer_transform. matrix2 += DMat2 :: IDENTITY * 1e-4 ; // TODO: Is this the cleanest way to handle this?
140- }
141-
142- let inverse = layer_transform. inverse ( ) ;
143- let mut bezier_iter = || bezier_iter ( ) . map ( |bezier| bezier. apply_transformation ( |point| inverse. transform_point2 ( point) ) ) ;
144-
145- match self . target_type ( ) {
146- ClickTargetType :: Subpath ( subpath) => {
147- // Check if outlines intersect
148- let outline_intersects = |path_segment : bezier_rs:: Bezier | bezier_iter ( ) . any ( |line| !path_segment. intersections ( & line, None , None ) . is_empty ( ) ) ;
149- if subpath. iter ( ) . any ( outline_intersects) {
150- return true ;
151- }
152- // Check if selection is entirely within the shape
153- if subpath. closed ( ) && bezier_iter ( ) . next ( ) . is_some_and ( |bezier| subpath. contains_point ( bezier. start ) ) {
154- return true ;
155- }
156-
157- // Check if shape is entirely within selection
158- let any_point_from_subpath = subpath. manipulator_groups ( ) . first ( ) . map ( |group| group. anchor ) ;
159- any_point_from_subpath. is_some_and ( |shape_point| bezier_iter ( ) . map ( |bezier| bezier. winding ( shape_point) ) . sum :: < i32 > ( ) != 0 )
160- }
161- ClickTargetType :: FreePoint ( point) => bezier_iter ( ) . map ( |bezier : bezier_rs:: Bezier | bezier. winding ( point. position ) ) . sum :: < i32 > ( ) != 0 ,
162- }
163- }
164-
165- /// Does the click target intersect the point (accounting for stroke size)
166- pub fn intersect_point ( & self , point : DVec2 , layer_transform : DAffine2 ) -> bool {
167- let target_bounds = [ point - DVec2 :: splat ( self . stroke_width / 2. ) , point + DVec2 :: splat ( self . stroke_width / 2. ) ] ;
168- let intersects = |a : [ DVec2 ; 2 ] , b : [ DVec2 ; 2 ] | a[ 0 ] . x <= b[ 1 ] . x && a[ 1 ] . x >= b[ 0 ] . x && a[ 0 ] . y <= b[ 1 ] . y && a[ 1 ] . y >= b[ 0 ] . y ;
169- // This bounding box is not very accurate as it is the axis aligned version of the transformed bounding box. However it is fast.
170- if !self
171- . bounding_box
172- . is_some_and ( |loose| ( loose[ 0 ] - loose[ 1 ] ) . abs ( ) . cmpgt ( DVec2 :: splat ( 1e-4 ) ) . any ( ) && intersects ( ( layer_transform * Quad :: from_box ( loose) ) . bounding_box ( ) , target_bounds) )
173- {
174- return false ;
175- }
176-
177- // Allows for selecting lines
178- // TODO: actual intersection of stroke
179- let inflated_quad = Quad :: from_box ( target_bounds) ;
180- self . intersect_path ( || inflated_quad. bezier_lines ( ) , layer_transform)
181- }
182-
183- /// Does the click target intersect the point (not accounting for stroke size)
184- pub fn intersect_point_no_stroke ( & self , point : DVec2 ) -> bool {
185- // Check if the point is within the bounding box
186- if self
187- . bounding_box
188- . is_some_and ( |bbox| bbox[ 0 ] . x <= point. x && point. x <= bbox[ 1 ] . x && bbox[ 0 ] . y <= point. y && point. y <= bbox[ 1 ] . y )
189- {
190- // Check if the point is within the shape
191- match self . target_type ( ) {
192- ClickTargetType :: Subpath ( subpath) => subpath. closed ( ) && subpath. contains_point ( point) ,
193- ClickTargetType :: FreePoint ( free_point) => free_point. position == point,
194- }
195- } else {
196- false
197- }
198- }
199- }
200-
20144/// Mutable state used whilst rendering to an SVG
20245pub struct SvgRender {
20346 pub svg : Vec < SvgSegment > ,
0 commit comments