@@ -5,6 +5,7 @@ use core_types::registry::types::{Angle, IntegerCount, Length, Multiplier, Perce
55use core_types:: table:: { Table , TableRow , TableRowMut } ;
66use core_types:: transform:: { Footprint , Transform } ;
77use core_types:: { CloneVarArgs , Color , Context , Ctx , ExtractAll , ExtractVarArgs , OwnedContextImpl } ;
8+ use dyn_any:: DynAny ;
89use glam:: { DAffine2 , DVec2 } ;
910use graphic_types:: Vector ;
1011use graphic_types:: raster_types:: { CPU , GPU , Raster } ;
@@ -225,7 +226,31 @@ where
225226 content
226227}
227228
228- #[ node_macro:: node( category( "Instancing" ) , path( core_types:: vector) ) ]
229+ #[ derive( Default , Debug , Clone , Copy , PartialEq , Eq , serde:: Serialize , serde:: Deserialize , Hash , DynAny , node_macro:: ChoiceType ) ]
230+ #[ widget( Radio ) ]
231+ pub enum RepeatSpacingMethod {
232+ #[ default]
233+ #[ serde( rename = "span" ) ]
234+ Span ,
235+ #[ serde( rename = "envelope" ) ]
236+ Envelope ,
237+ #[ serde( rename = "pitch" ) ]
238+ Pitch ,
239+ #[ serde( rename = "gap" ) ]
240+ Gap ,
241+ }
242+
243+ #[ derive( Default , Debug , Clone , Copy , PartialEq , Eq , serde:: Serialize , serde:: Deserialize , Hash , DynAny , node_macro:: ChoiceType ) ]
244+ #[ widget( Radio ) ]
245+ pub enum AngularSpacingMethod {
246+ #[ default]
247+ #[ serde( rename = "span" ) ]
248+ Span ,
249+ #[ serde( rename = "pitch" ) ]
250+ Pitch ,
251+ }
252+
253+ #[ node_macro:: node( category( "Instancing" ) , path( graphene_core:: vector) , properties( "repeat_properties" ) ) ]
229254async fn repeat < I : ' n + Send + Clone > (
230255 _: impl Ctx ,
231256 // TODO: Implement other graphical types.
@@ -235,16 +260,28 @@ async fn repeat<I: 'n + Send + Clone>(
235260 direction : PixelSize ,
236261 angle : Angle ,
237262 #[ default( 5 ) ] count : IntegerCount ,
263+ #[ default( RepeatSpacingMethod :: Span ) ] spacing_method : RepeatSpacingMethod ,
264+ #[ default( 0. ) ]
265+ #[ unit( " px" ) ]
266+ width : f64 ,
238267) -> Table < I > {
239268 let angle = angle. to_radians ( ) ;
240269 let count = count. max ( 1 ) ;
241270 let total = ( count - 1 ) as f64 ;
271+ let direction_normalized = direction. normalize ( ) ;
272+
273+ let ( pitch, offset) = match spacing_method {
274+ RepeatSpacingMethod :: Span => ( direction. length ( ) / total. max ( 1. ) , DVec2 :: ZERO ) ,
275+ RepeatSpacingMethod :: Envelope => ( ( direction. length ( ) - width) / total. max ( 1. ) , width / 2. * direction_normalized) ,
276+ RepeatSpacingMethod :: Pitch => ( direction. length ( ) , DVec2 :: ZERO ) ,
277+ RepeatSpacingMethod :: Gap => ( direction. length ( ) + width, DVec2 :: ZERO ) ,
278+ } ;
242279
243280 let mut result_table = Table :: new ( ) ;
244281
245282 for index in 0 ..count {
246- let angle = index as f64 * angle / total;
247- let translation = index as f64 * direction / total ;
283+ let angle = index as f64 * angle / total. max ( 1. ) ;
284+ let translation = offset + index as f64 * pitch * direction_normalized ;
248285 let transform = DAffine2 :: from_angle ( angle) * DAffine2 :: from_translation ( translation) ;
249286
250287 for row in instance. iter ( ) {
@@ -261,22 +298,34 @@ async fn repeat<I: 'n + Send + Clone>(
261298 result_table
262299}
263300
264- #[ node_macro:: node( category( "Instancing" ) , path( core_types :: vector) ) ]
301+ #[ node_macro:: node( category( "Instancing" ) , path( graphene_core :: vector) , properties ( "circular_repeat_properties" ) ) ]
265302async fn circular_repeat < I : ' n + Send + Clone > (
266303 _: impl Ctx ,
267304 #[ implementations( Table <Graphic >, Table <Vector >, Table <Raster <CPU >>, Table <Color >, Table <GradientStops >) ] instance : Table < I > ,
268- start_angle : Angle ,
305+ #[ default( 0. ) ] start_angle : Angle ,
306+ #[ default( 360. ) ] end_angle : Angle ,
269307 #[ unit( " px" ) ]
270308 #[ default( 5 ) ]
271309 radius : f64 ,
272310 #[ default( 5 ) ] count : IntegerCount ,
311+ #[ default( AngularSpacingMethod :: Span ) ] angular_spacing_method : AngularSpacingMethod ,
273312) -> Table < I > {
274313 let count = count. max ( 1 ) ;
314+ let start_rad = start_angle. to_radians ( ) ;
315+ let end_rad = end_angle. to_radians ( ) ;
316+ let total_angle = end_rad - start_rad;
317+ let total = ( count - 1 ) as f64 ;
318+
319+ let angular_pitch = match angular_spacing_method {
320+ AngularSpacingMethod :: Span => total_angle / total. max ( 1. ) ,
321+ AngularSpacingMethod :: Pitch => total_angle / count as f64 ,
322+ } ;
275323
276324 let mut result_table = Table :: new ( ) ;
277325
278326 for index in 0 ..count {
279- let angle = DAffine2 :: from_angle ( ( TAU / count as f64 ) * index as f64 + start_angle. to_radians ( ) ) ;
327+ let angle_rad = start_rad + index as f64 * angular_pitch;
328+ let angle = DAffine2 :: from_angle ( angle_rad) ;
280329 let translation = DAffine2 :: from_translation ( radius * DVec2 :: Y ) ;
281330 let transform = angle * translation;
282331
@@ -2417,6 +2466,8 @@ mod test {
24172466 direction,
24182467 0. ,
24192468 count,
2469+ super :: RepeatSpacingMethod :: Span ,
2470+ 0. ,
24202471 )
24212472 . await ;
24222473 let vector_table = super :: flatten_path ( Footprint :: default ( ) , repeated) . await ;
@@ -2436,6 +2487,8 @@ mod test {
24362487 direction,
24372488 0. ,
24382489 count,
2490+ super :: RepeatSpacingMethod :: Span ,
2491+ 0. ,
24392492 )
24402493 . await ;
24412494 let vector_table = super :: flatten_path ( Footprint :: default ( ) , repeated) . await ;
@@ -2447,7 +2500,16 @@ mod test {
24472500 }
24482501 #[ tokio:: test]
24492502 async fn circular_repeat ( ) {
2450- let repeated = super :: circular_repeat ( Footprint :: default ( ) , vector_node_from_bezpath ( Rect :: new ( -1. , -1. , 1. , 1. ) . to_path ( DEFAULT_ACCURACY ) ) , 45. , 4. , 8 ) . await ;
2503+ let repeated = super :: circular_repeat (
2504+ Footprint :: default ( ) ,
2505+ vector_node_from_bezpath ( Rect :: new ( -1. , -1. , 1. , 1. ) . to_path ( DEFAULT_ACCURACY ) ) ,
2506+ 45. ,
2507+ 360. ,
2508+ 4. ,
2509+ 8 ,
2510+ super :: AngularSpacingMethod :: Span ,
2511+ )
2512+ . await ;
24512513 let vector_table = super :: flatten_path ( Footprint :: default ( ) , repeated) . await ;
24522514 let vector = vector_table. iter ( ) . next ( ) . unwrap ( ) . element ;
24532515 assert_eq ! ( vector. region_manipulator_groups( ) . count( ) , 8 ) ;
@@ -2588,7 +2650,7 @@ mod test {
25882650 #[ tokio:: test]
25892651 async fn morph ( ) {
25902652 let rectangle = vector_node_from_bezpath ( Rect :: new ( 0. , 0. , 100. , 100. ) . to_path ( DEFAULT_ACCURACY ) ) ;
2591- let rectangles = super :: repeat ( Footprint :: default ( ) , rectangle, DVec2 :: new ( -100. , -100. ) , 0. , 2 ) . await ;
2653+ let rectangles = super :: repeat ( Footprint :: default ( ) , rectangle, DVec2 :: new ( -100. , -100. ) , 0. , 2 , super :: RepeatSpacingMethod :: Span , 0. ) . await ;
25922654 let morphed = super :: morph ( Footprint :: default ( ) , rectangles, 0.5 ) . await ;
25932655 let element = morphed. iter ( ) . next ( ) . unwrap ( ) . element ;
25942656 assert_eq ! (
0 commit comments