@@ -7,7 +7,7 @@ use crate::instances::{Instance, InstanceMut, Instances};
use crate ::raster ::image ::ImageFrameTable ;
use crate ::registry ::types ::{ Angle , Fraction , IntegerCount , Length , Multiplier , Percentage , PixelLength , PixelSize , SeedValue } ;
use crate ::renderer ::GraphicElementRendered ;
use crate ::transform ::{ Footprint , ReferencePoint , Transform , TransformMut };
use crate ::transform ::{ Footprint , ReferencePoint , Transform } ;
use crate ::vector ::PointDomain ;
use crate ::vector ::misc ::dvec2_to_point ;
use crate ::vector ::style ::{ LineCap , LineJoin } ;
@@ -312,56 +312,56 @@ async fn copy_to_points<I: 'n + Send>(
where
Instances < I > : GraphicElementRendered ,
{
let points_transform = points . transform ( ) ;
let points_list = points . instance_ref_iter ( ) . flat_map ( | element | element . instance . point_domain . positions ( ) ) ;
let mut result_table = GraphicGroupTable ::default ( ) ;
let random_scale_difference = random_scale_max - random_scale_min ;
let instance_bounding_box = instance . bounding_box ( DAffine2 ::IDENTITY , false ) . unwrap_or_default ( ) ;
let instance_center = - 0.5 * ( instance_bounding_box [ 0 ] + instance_bounding_box [ 1 ] ) ;
let mut scale_rng = rand ::rngs ::StdRng ::seed_from_u64 ( random_scale_seed . into ( ) ) ;
let mut rotation _rng = rand ::rngs ::StdRng ::seed_from_u64 ( random_rotation _seed . into ( ) ) ;
for point_instance in points . instance_iter ( ) {
let mut scale _rng = rand ::rngs ::StdRng ::seed_from_u64 ( random_scale _seed . into ( ) ) ;
let mut rotation_rng = rand ::rngs ::StdRng ::seed_from_u64 ( random_rotation_seed . into ( ) ) ;
let do_scale = random_scale_difference . abs ( ) > 1e-6 ;
let do_rotation = random_rotation . abs ( ) > 1e-6 ;
let do_scale = random_scale_difference . abs ( ) > 1e-6 ;
let do_rotation = random_rotation . abs ( ) > 1e-6 ;
let mut result_table = GraphicGroupTable ::default ( ) ;
let points_transform = point_instance . transform ;
for & point in point_instance . instance . point_domain . positions ( ) {
let center_transform = DAffine2 ::from_translation ( instance_center ) ;
for & point in points_list . into_iter ( ) {
let center_transform = DAffine2 ::from_translation ( instance_center ) ;
let translation = points_transform . transform_point2 ( point ) ;
let transl ation = points_transform . transform_point2 ( point ) ;
let rotation = if do_rotation {
let degrees = ( rotation_rng . random ::< f64 > ( ) - 0.5 ) * random_rotation ;
degrees / 360. * std ::f64 ::consts ::TAU
} else {
0.
} ;
let scale = if do_scale {
if random_scale_bias . abs ( ) < 1e-6 {
// Linear
random_scale_min + scale_rng . random ::< f64 > ( ) * random_scale_difference
let rot ation = if do_rotation {
let degrees = ( rotation_rng . random ::< f64 > ( ) - 0.5 ) * random_rotation ;
degrees / 360. * std ::f64 ::consts ::TAU
} else {
// Weighted (see <https://www.desmos.com/calculator/gmavd3m9bd>)
let horizontal_scale_factor = 1. - 2_ f64 . powf ( random_scale_bias ) ;
let scale_factor = ( 1. - scale_rng . random ::< f64 > ( ) * horizontal_scale_factor ) . log2 ( ) / random_scale_bias ;
random_scale_min + scale_factor * random_scale_difference
}
} else {
random_scale_min
} ;
0.
} ;
let transform = DAffine2 ::from_scale_angle_translation ( DVec2 ::splat ( scale ) , rotation , translation ) * center_transform ;
let scale = if do_scale {
if random_scale_bias . abs ( ) < 1e-6 {
// Linear
random_scale_min + scale_rng . random ::< f64 > ( ) * random_scale_difference
} else {
// Weighted (see <https://www.desmos.com/calculator/gmavd3m9bd>)
let horizontal_scale_factor = 1. - 2_ f64 . powf ( random_scale_bias ) ;
let scale_factor = ( 1. - scale_rng . random ::< f64 > ( ) * horizontal_scale_factor ) . log2 ( ) / random_scale_bias ;
random_scale_min + scale_factor * random_scale_difference
}
} else {
random_scale_min
} ;
result_table . push ( Instance {
instance : instance . to_graphic_element ( ) . clone ( ) ,
transform ,
alpha_blending : Default ::default ( ) ,
source_node_id : None ,
} ) ;
let transform = DAffine2 ::from_scale_angle_translation ( DVec2 ::splat ( scale ) , rotation , translation ) * center_transform ;
result_table . push ( Instance {
instance : instance . to_graphic_element ( ) . clone ( ) ,
transform ,
alpha_blending : Default ::default ( ) ,
source_node_id : None ,
} ) ;
}
}
result_table
@@ -445,12 +445,11 @@ async fn round_corners(
#[ default(5.) ]
min_angle_threshold : Angle ,
) -> VectorDataTable {
let source_transform = source . transform ( ) ;
let source_transform_inverse = source_transform . inverse ( ) ;
let mut result_table = VectorDataTable ::empty ( ) ;
for source in source . instance_ref_iter ( ) {
let source_transform = * source . transform ;
let source_transform_inverse = source_transform . inverse ( ) ;
let source = source . instance ;
let upstream_graphic_group = source . upstream_graphic_group . clone ( ) ;
@@ -1418,181 +1417,193 @@ async fn subpath_segment_lengths(_: impl Ctx, vector_data: VectorDataTable) -> V
}
#[ node_macro::node(name( " Spline " ), category( " Vector " ), path(graphene_core::vector)) ]
async fn spline ( _ : impl Ctx , mut vector_data : VectorDataTable ) -> VectorDataTable {
let original_transform = v ector_d ata. transform ( ) ;
let vector_data = vector_data . one_instance_mut ( ) . instance ;
async fn spline ( _ : impl Ctx , vector_data : VectorDataTable ) -> VectorDataTable {
let mut result_table = V ectorD ataTable ::empty ( ) ;
// Exit early if there are no points to generate splines from.
if vector_data . point_domain . positions ( ) . is_empty ( ) {
for mut vector_data_instance in vector_data . instance_iter ( ) {
// Exit early if there are no points to generate splines from.
if vector_data_instance . instance . point_domain . positions ( ) . is_empty ( ) {
continue ;
}
let mut segment_domain = SegmentDomain ::default ( ) ;
for subpath in vector_data_instance . instance . stroke_bezier_paths ( ) {
let positions = subpath . manipulator_groups ( ) . iter ( ) . map ( | group | group . anchor ) . collect ::< Vec < _ > > ( ) ;
let closed = subpath . closed ( ) & & positions . len ( ) > 2 ;
// Compute control point handles for Bezier spline.
let first_handles = if closed {
bezier_rs ::solve_spline_first_handle_closed ( & positions )
} else {
bezier_rs ::solve_spline_first_handle_open ( & positions )
} ;
let stroke_id = StrokeId ::ZERO ;
// Create segments with computed Bezier handles and add them to vector data.
for i in 0 .. ( positions . len ( ) - if closed { 0 } else { 1 } ) {
let next_index = ( i + 1 ) % positions . len ( ) ;
let start_index = vector_data_instance . instance . point_domain . resolve_id ( subpath . manipulator_groups ( ) [ i ] . id ) . unwrap ( ) ;
let end_index = vector_data_instance . instance . point_domain . resolve_id ( subpath . manipulator_groups ( ) [ next_index ] . id ) . unwrap ( ) ;
let handle_start = first_handles [ i ] ;
let handle_end = positions [ next_index ] * 2. - first_handles [ next_index ] ;
let handles = bezier_rs ::BezierHandles ::Cubic { handle_start , handle_end } ;
segment_domain . push ( SegmentId ::generate ( ) , start_index , end_index , handles , stroke_id ) ;
}
}
vector_data_instance . instance . segment_domain = segment_domain ;
result_table . push ( vector_data_instance ) ;
}
// TODO: remove after pt6 of instance table refactor
if result_table . is_empty ( ) {
return VectorDataTable ::new ( VectorData ::empty ( ) ) ;
}
let mut segment_domain = SegmentDomain ::default ( ) ;
for subpath in vector_data . stroke_bezier_paths ( ) {
let positions = subpath . manipulator_groups ( ) . iter ( ) . map ( | group | group . anchor ) . collect ::< Vec < _ > > ( ) ;
let closed = subpath . closed ( ) & & positions . len ( ) > 2 ;
// Compute control point handles for Bezier spline.
let first_handles = if closed {
bezier_rs ::solve_spline_first_handle_closed ( & positions )
} else {
bezier_rs ::solve_spline_first_handle_open ( & positions )
} ;
let stroke_id = StrokeId ::ZERO ;
// Create segments with computed Bezier handles and add them to vector data.
for i in 0 .. ( positions . len ( ) - if closed { 0 } else { 1 } ) {
let next_index = ( i + 1 ) % positions . len ( ) ;
let start_index = vector_data . point_domain . resolve_id ( subpath . manipulator_groups ( ) [ i ] . id ) . unwrap ( ) ;
let end_index = vector_data . point_domain . resolve_id ( subpath . manipulator_groups ( ) [ next_index ] . id ) . unwrap ( ) ;
let handle_start = first_handles [ i ] ;
let handle_end = positions [ next_index ] * 2. - first_handles [ next_index ] ;
let handles = bezier_rs ::BezierHandles ::Cubic { handle_start , handle_end } ;
segment_domain . push ( SegmentId ::generate ( ) , start_index , end_index , handles , stroke_id ) ;
}
}
vector_data . segment_domain = segment_domain ;
let mut result = VectorDataTable ::new ( vector_data . clone ( ) ) ;
* result . transform_mut ( ) = original_transform ;
result
result_table
}
#[ node_macro::node(category( " Vector " ), path(graphene_core::vector)) ]
async fn jitter_points ( _ : impl Ctx , vector_data : VectorDataTable , #[ default(5.) ] amount : f64 , seed : SeedValue ) -> VectorDataTable {
let vector_data_transform = v ector_d ata. transform ( ) ;
let mut vector_data = vector_data . one_instance_ref ( ) . instance . clone ( ) ;
let mut result_table = V ectorD ataTable ::empty ( ) ;
let inverse_trans form = ( vector_data_transform . matrix2 . determinant ( ) ! = 0. ) . then ( | | vector_data_transform . inverse ( ) ) . unwrap_or_default ( ) ;
for mut vector_data_instance in vector_data . instance_iter ( ) {
let mut rng = rand ::rngs ::StdRng ::seed_from_u64 ( seed . into ( ) ) ;
let mut rng = rand ::rngs ::StdRng ::seed_from_u64 ( seed . into ( ) ) ;
let vector_data_transform = vector_data_instance . transform ;
let inverse_transform = ( vector_data_transform . matrix2 . determinant ( ) ! = 0. ) . then ( | | vector_data_transform . inverse ( ) ) . unwrap_or_default ( ) ;
let deltas = ( 0 .. vector_data . point_domain . positions ( ) . len ( ) )
. map ( | _ | {
let angle = rng . random ::< f64 > ( ) * std ::f64 ::consts ::TAU ;
let deltas = ( 0 .. vector_data_instance . instance . point_domain . positions ( ) . len ( ) )
. map ( | _ | {
let angle = rng . random ::< f64 > ( ) * std ::f64 ::consts ::TAU ;
inverse_transform . transform_vector2 ( DVec2 ::from_angle ( angle ) * rng . random ::< f64 > ( ) * amount )
} )
. collect ::< Vec < _ > > ( ) ;
let mut already_applied = vec! [ false ; vector_data . point_domain . positions ( ) . len ( ) ] ;
inverse_transform . transform_vector2 ( DVec2 ::from_angle ( angle ) * rng . random ::< f64 > ( ) * amount )
} )
. collect ::< Vec < _ > > ( ) ;
let mut already_applied = vec! [ false ; vector_data_instance . instance . point_domain . positions ( ) . len ( ) ] ;
for ( handles , start , end ) in vector_data . segment_domain . handles_and_points_mut ( ) {
let start_delta = deltas [ * start ] ;
let end_delta = deltas [ * end ] ;
for ( handles , start , end ) in vector_data_instance . instance . segment_domain . handles_and_points_mut ( ) {
let start_delta = deltas [ * start ] ;
let end_delta = deltas [ * end ] ;
if ! already_applied [ * start ] {
let start_position = vector_data . point_domain . positions ( ) [ * start ] ;
vector_data . point_domain . set_position ( * start , start_position + start_delta ) ;
already_applied [ * start ] = true ;
}
if ! already_applied [ * end ] {
let end_position = vector_data . point_domain . positions ( ) [ * end ] ;
vector_data . point_domain . set_position ( * end , end_position + end_delta ) ;
already_applied [ * end ] = true ;
}
match handles {
bezier_rs ::BezierHandles ::Cubic { handle_start , handle_end } = > {
* handle_start + = start_delta ;
* handle_end + = end_delta ;
if ! already_applied [ * start ] {
let start_position = vector_data_instance . instance . point_domain . positions ( ) [ * start ] ;
vector_data_instance . instance . point_domain . set_position ( * start , start_position + start_delta ) ;
already_applied [ * start ] = true ;
}
bezier_rs ::BezierHandles ::Quadratic { handle } = > {
* handle = vector_data_transform . transform_point2 ( * handle ) + ( start_delta + end_delta ) / 2. ;
if ! already_applied [ * end ] {
let end_position = vector_data_instance . instance . point_domain . positions ( ) [ * end ] ;
vector_data_instance . instance . point_domain . set_position ( * end , end_position + end_delta ) ;
already_applied [ * end ] = true ;
}
match handles {
bezier_rs ::BezierHandles ::Cubic { handle_start , handle_end } = > {
* handle_start + = start_delta ;
* handle_end + = end_delta ;
}
bezier_rs ::BezierHandles ::Quadratic { handle } = > {
* handle = vector_data_instance . transform . transform_point2 ( * handle ) + ( start_delta + end_delta ) / 2. ;
}
bezier_rs ::BezierHandles ::Linear = > { }
}
bezier_rs ::BezierHandles ::Linear = > { }
}
vector_data_instance . instance . style . set_stroke_transform ( DAffine2 ::IDENTITY ) ;
result_table . push ( vector_data_instance ) ;
}
vector_data . style . set_stroke_transform ( DAffine2 ::IDENTITY ) ;
let mut result = VectorDataTable ::new ( vector_data . clone ( ) ) ;
* result . transform_mut ( ) = vector_data_transform ;
result
result_table
}
#[ node_macro::node(category( " Vector " ), path(graphene_core::vector)) ]
async fn morph ( _ : impl Ctx , source : VectorDataTable , #[ expose ] target : VectorDataTable , #[ default(0.5) ] time : Fraction ) -> VectorDataTable {
let mut source = source ;
let mut target = target ;
let time = time . clamp ( 0. , 1. ) ;
let mut result_table = VectorDataTable ::default ( ) ;
let mut result_table = VectorDataTable ::empty ( ) ;
// Lerp styles
let source_alpha_blending = source . one_instance_ref ( ) . alpha_blending ;
let target_alpha_blending = target . one_instance_ref ( ) . alpha_blending ;
* result_table . one_instance_mut ( ) . alpha_blending = if time < 0.5 { * source_alpha_blending } else { * target_alpha_blending } ;
result_table . one_instance_mut ( ) . instance . style = source . one_instance_ref ( ) . instance . style . lerp ( & target . one_instance_ref ( ) . instance . style , time ) ;
for ( source_instance , target_instance ) in source . instance_iter ( ) . zip ( target . instance_iter ( ) ) {
let mut vector_data_instance = VectorData ::default ( ) ;
// Before and after transform s
let source_transform = * source. one_instance_ref ( ) . transform ;
let target_transform = * target . one_ instance_ref ( ) . transform ;
// Lerp style s
let vector_data_alpha_blending = source_instance . alpha_blending . lerp ( & target_instance . alpha_blending , time as f32 ) ;
vector_data_instance . style = source_instance . instance. style . lerp ( & target_instance . instance . style , time ) ;
// Before and after path s
let source_paths = source. one _instance_mut ( ) . instance . stroke_bezier_paths ( ) ;
let target_paths = target. one _instance_mut ( ) . instance . stroke_bezier_paths ( ) ;
for ( mut source_path , mut target_path ) in source_paths . zip ( target_paths ) {
source_path . apply_transform ( source_transform ) ;
target_path . apply_transform ( target_transform ) ;
// Before and after transform s
let source_transform = source_instance. transform ;
let target_transform = target_instance. transform ;
// Align point counts by inserting mid‐ segment points until their counts match
whi le source_path. manipulator_groups ( ) . len ( ) < target_path . manipulator_groups ( ) . len ( ) {
let last = source_path . len ( ) - 1 ;
source_path . insert ( SubpathTValue ::Parametric { segment_index : last , t : 0.5 } ) ;
}
while target_path . manipulator_groups ( ) . len ( ) < source_path . manipulator_groups ( ) . len ( ) {
let last = target_path . len ( ) - 1 ;
target_path . insert ( SubpathTValue ::Parametric { segment_index : last , t : 0.5 } ) ;
// Before and after paths
let source_paths = source_instance . instance . stroke_bezier_paths ( ) ;
let target_paths = target_instance . instance . stroke_bezier_paths ( ) ;
for ( mut source_path , mut target_path ) in source_paths . zip ( target_paths ) {
source_path . apply_transform ( source_transform ) ;
target_path . apply_transform ( target_transform ) ;
// Align point counts by inserting mid‐ segment points until their counts match
while source_path . manipulator_groups ( ) . len ( ) < target_path . manipulator_groups ( ) . len ( ) {
let last = source_path . len ( ) - 1 ;
source_path . insert ( SubpathTValue ::Parametric { segment_index : last , t : 0.5 } ) ;
}
while target_path . manipulator_groups ( ) . len ( ) < source_path . manipulator_groups ( ) . len ( ) {
let last = target_path . len ( ) - 1 ;
target_path . insert ( SubpathTValue ::Parametric { segment_index : last , t : 0.5 } ) ;
}
// Interpolate anchors and handles
for ( source_manipulators , target_manipulators ) in source_path . manipulator_groups_mut ( ) . iter_mut ( ) . zip ( target_path . manipulator_groups ( ) ) {
let source_anchor = source_manipulators . anchor ;
let target_anchor = target_manipulators . anchor ;
source_manipulators . anchor = source_anchor . lerp ( target_anchor , time ) ;
let source_in_handle = source_manipulators . in_handle . unwrap_or ( source_anchor ) ;
let target_in_handle = target_manipulators . in_handle . unwrap_or ( target_anchor ) ;
source_manipulators . in_handle = Some ( source_in_handle . lerp ( target_in_handle , time ) ) ;
let source_out_handle = source_manipulators . out_handle . unwrap_or ( source_anchor ) ;
let target_out_handle = target_manipulators . out_handle . unwrap_or ( target_anchor ) ;
source_manipulators . out_handle = Some ( source_out_handle . lerp ( target_out_handle , time ) ) ;
}
vector_data_instance . append_subpath ( source_path . clone ( ) , true ) ;
}
// Interpolate anchors and handles
for ( source_manipulators , target_manipulators ) in source_path . manipulator_groups_mut ( ) . iter_mut ( ) . zip ( target_path . manipulator_groups ( ) ) {
let source_anchor = source_manipulators . anchor ;
let target_anchor = target_manipulators . anchor ;
source_manipulators . anchor = source_anchor . lerp ( target_anchor , time ) ;
// Deal with unmatched extra paths by collapsing them
let source_paths_count = source_instance . instance . stroke_bezier_paths ( ) . count ( ) ;
let target_paths_count = target_instance . instance . stroke_bezier_paths ( ) . count ( ) ;
let source_paths = source_instance . instance . stroke_bezier_paths ( ) . skip ( target_paths_count ) ;
let target_paths = target_instance . instance . stroke_bezier_paths ( ) . skip ( source_paths_count ) ;
let source_in_handle = source_manipulators . in_handle . unwrap_or ( source_anchor ) ;
let target_in_handle = target_manipulators . in_handle . unwrap_ or( target_anch or) ;
source_manipulators . in_handle = Some ( source_in_handle . ler p( target_in_handle , time ) ) ;
let source_out_handle = source_manipulators . out_handle . unwrap_or ( source_anchor ) ;
let target_out_handle = target_manipulators . out _handle. unwrap_or ( target_anchor ) ;
source_manipulators . out_handle = Some ( source_ out_handle. ler p( target_out_handle , time ) ) ;
for mut source_path in source_paths {
source_path . apply_transf orm ( source_transf orm ) ;
let end = source_path . manipulator_groups ( ) . last ( ) . map ( | group | grou p. anchor ) . unwrap_or_default ( ) ;
for group in source_path . manipulator_groups_mut ( ) {
group . anchor = group . anchor . lerp ( end , time ) ;
group . in_handle = group . in _handle. map ( | handle | handle . lerp ( end , time ) ) ;
group . out_handle = group . out_handle . ma p( | handle | handle . lerp ( end , time ) ) ;
}
vector_data_instance . append_subpath ( source_path , true ) ;
}
for mut target_path in target_paths {
target_path . apply_transform ( target_transform ) ;
let start = target_path . manipulator_groups ( ) . first ( ) . map ( | group | group . anchor ) . unwrap_or_default ( ) ;
for group in target_path . manipulator_groups_mut ( ) {
group . anchor = start . lerp ( group . anchor , time ) ;
group . in_handle = group . in_handle . map ( | handle | start . lerp ( handle , time ) ) ;
group . out_handle = group . out_handle . map ( | handle | start . lerp ( handle , time ) ) ;
}
vector_data_instance . append_subpath ( target_path , true ) ;
}
result_table . one_instance_mut ( ) . instance . append_subpath ( source_path . clone ( ) , true ) ;
}
// Deal with unmatched extra paths by collapsing them
let source_paths_count = source . one_instance_ref ( ) . instance . stroke_bezier_paths ( ) . count ( ) ;
let target_paths_count = target . one_instance_ref ( ) . instance . stroke_bezier_paths ( ) . count ( ) ;
let source_paths = source . one_instance_mut ( ) . instance . stroke_bezier_paths ( ) . skip ( target_paths_count ) ;
let target_paths = target . one_instance_mut ( ) . instance . stroke_bezier_paths ( ) . skip ( source_paths_count ) ;
for mut source_path in source_paths {
source_path . apply_transform ( source_transform ) ;
let end = source_path . manipulator_groups ( ) . last ( ) . map ( | group | group . anchor ) . unwrap_or_default ( ) ;
for group in source_path . manipulator_groups_mut ( ) {
group . anchor = group . anchor . lerp ( end , time ) ;
group . in_handle = group . in_handle . map ( | handle | handle . lerp ( end , time ) ) ;
group . out_handle = group . out_handle . map ( | handle | handle . lerp ( end , time ) ) ;
}
result_table . one_instance_mut ( ) . instance . append_subpath ( source_path , true ) ;
}
for mut target_path in target_paths {
target_path . apply_transform ( target_transform ) ;
let start = target_path . manipulator_groups ( ) . first ( ) . map ( | group | group . anchor ) . unwrap_or_default ( ) ;
for group in target_path . manipulator_groups_mut ( ) {
group . anchor = start . lerp ( group . anchor , time ) ;
group . in_handle = group . in_handle . map ( | handle | start . lerp ( handle , time ) ) ;
group . out_handle = group . out_handle . map ( | handle | start . lerp ( handle , time ) ) ;
}
result_table . one_instance_mut ( ) . instance . append_subpath ( target_path , true ) ;
result_table . push ( Instance {
instance : vector_data_instance ,
alpha_blending : vector_data_alpha_blending ,
.. Default ::default ( )
} ) ;
}
result_table
@@ -1638,7 +1649,7 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2,
point_domain . push ( next_id . next_id ( ) , pos ) ;
// Add a new segment to be created later
new_segments . push ( [ new_index , original_index ] )
new_segments . push ( [ new_index , original_index ] ) ;
}
}
@@ -1707,12 +1718,16 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2,
#[ node_macro::node(category( " Vector " ), path(graphene_core::vector)) ]
fn bevel ( _ : impl Ctx , source : VectorDataTable , #[ default(10.) ] distance : Length ) -> VectorDataTable {
let source_transform = source . transform ( ) ;
let source = source . one_instance_ref ( ) . instance ;
let mut result_table = VectorDataTable ::empty ( ) ;
let mut result = VectorDataTable ::new ( bevel_algorithm ( source . clone ( ) , source_transform , di stance) ) ;
* result. transform_mut ( ) = source_transform ;
result
for source_instance in source . in stance_iter ( ) {
result_table . push ( Instance {
instance : bevel_algorithm ( source_instance . instance , source_instance . transform , distance ) ,
.. Default ::default ( )
} ) ;
}
result_table
}
#[ node_macro::node(category( " Vector " ), path(graphene_core::vector)) ]
@@ -1734,15 +1749,14 @@ fn point_inside(_: impl Ctx, source: VectorDataTable, point: DVec2) -> bool {
#[ node_macro::node(name( " Merge by Distance " ), category( " Vector " ), path(graphene_core::vector)) ]
fn merge_by_distance ( _ : impl Ctx , source : VectorDataTable , #[ default(10.) ] distance : Length ) -> VectorDataTable {
let source_transform = source . transform ( ) ;
let mut source = source . one_instance_ref ( ) . instance . clone ( ) ;
let mut result_table = VectorDataTable ::empty ( ) ;
source . merge_by_distance ( distance ) ;
for mut source_instance in source . instance_iter ( ) {
source_instance . instance . merge_by_distance ( distance ) ;
result_table . push ( source_instance ) ;
}
let mut result = VectorDataTable ::new ( source ) ;
* result . transform_mut ( ) = source_transform ;
result
result_table
}
#[ node_macro::node(category( " Vector " ), path(graphene_core::vector)) ]
@@ -1750,16 +1764,13 @@ async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<
let new_ctx = OwnedContextImpl ::from ( ctx ) . with_footprint ( Footprint ::default ( ) ) . into_context ( ) ;
let vector_data = vector_data . eval ( new_ctx ) . await ;
let vector_data_transform = vector_data . transform ( ) ;
let vector_data = vector_data . one_ instance_ref( ) . instance ;
let mut area = 0. ;
let scale = vecto r_d ata_transform . decompose_scale ( ) ;
for subpath in vector_data . stroke_bezier_paths ( ) {
area + = subpath . area ( Some ( 1e-3 ) , Some ( 1e-3 ) ) ;
}
area * scale [ 0 ] * scale [ 1 ]
vector_data
. instance_ref_iter ( )
. map ( | vector_data_instance | {
let scale = vector_data_instance . transform . decompose_scale ( ) ;
vector_data_instance . instance . stroke_bezie r_p aths ( ) . map ( | subpath | subpath . area ( Some ( 1e-3 ) , Some ( 1e-3 ) ) ) . sum ::< f64 > ( ) * scale . x * scale . y
} )
. sum ( )
}
#[ node_macro::node(category( " Vector " ), path(graphene_core::vector)) ]
@@ -1767,49 +1778,52 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl N
let new_ctx = OwnedContextImpl ::from ( ctx ) . with_footprint ( Footprint ::default ( ) ) . into_context ( ) ;
let vector_data = vector_data . eval ( new_ctx ) . await ;
let vector_data_transform = vector_data . transform ( ) ;
let vector_data = vector_data . one_instance_ref ( ) . instance ;
if vector_data . is_empty ( ) {
return DVec2 ::ZERO ;
}
if centroid_type = = CentroidType ::Area {
let mut area = 0. ;
let mut centroid = DVec2 ::ZERO ;
for subpath in vector_data . stroke_bezier_paths ( ) {
if let Some ( ( subpath_centroid , subpath_area ) ) = subpath . area_centroid_and_area ( Some ( 1e-3 ) , Some ( 1e-3 ) ) {
if subpath_area = = 0. {
continue ;
}
a rea + = subpath_area ;
c entroid + = subpath_area * subpath_centroid ;
// All subpath centroid positions added together as if they were vectors from the origin.
let mut centroid = DVec2 ::ZERO ;
// Cumulative area or length of all subpaths
let mut sum = 0. ;
for vector_data_instance in vector_data . instance_ref_iter ( ) {
for subpath in vector_data_instance . instance . stroke_bezier_paths ( ) {
let partial = match centroid_type {
CentroidType ::A rea = > subpath. area_centroid_and_area ( Some ( 1e-3 ) , Some ( 1e-3 ) ) . filter ( | ( _ , area ) | * area > 0. ) ,
C entroidType ::Length = > subpath. length_centroid_and_length ( None , true ) ,
} ;
if let Some ( ( subpath_centroid , area_or_length ) ) = partial {
let subpath_centroid = vector_data_instance . transform . transform_point2 ( subpath_centroid ) ;
sum + = area_or_length ;
centroid + = area_or_length * subpath_centroid ;
}
}
if area ! = 0. {
centroid / = area ;
return vector_data_transform . transform_point2 ( centroid ) ;
}
}
let mut length = 0. ;
let mut centroid = DVec2 ::ZERO ;
for subpath in vector_data . stroke_bezier_paths ( ) {
if let Some ( ( subpath_centroid , subpath_length ) ) = subpath . length_centroid_and_length ( None , true ) {
length + = subpath_length ;
centroid + = subpath_length * subpath_centroid ;
}
if sum > 0. {
centroid / sum
}
// Without a summed denominator, return the average of all positions instead
else {
let mut count : usize = 0 ;
if length ! = 0. {
centroid / = length ;
return vector_data_transform . transform_point2 ( centroid ) ;
let summed_positions = vector_data
. instance_ref_iter ( )
. flat_map ( | vector_data_instance | {
vector_data_instance
. instance
. point_domain
. positions ( )
. iter ( )
. map ( | & p | vector_data_instance . transform . transform_point2 ( p ) )
} )
. inspect ( | _ | count + = 1 )
. sum ::< DVec2 > ( ) ;
if count ! = 0 { summed_positions / ( count as f64 ) } else { DVec2 ::ZERO }
}
let positions = vector_data . point_domain . positions ( ) ;
if ! positions . is_empty ( ) {
let centroid = positions . iter ( ) . sum ::< DVec2 > ( ) / ( positions . len ( ) as f64 ) ;
return vector_data_transform . transform_point2 ( centroid ) ;
}
DVec2 ::ZERO
}
#[ cfg(test) ]
@@ -1885,7 +1899,7 @@ mod test {
// Test a VectorData with non-zero rotation
let square = VectorData ::from_subpath ( Subpath ::new_rect ( DVec2 ::NEG_ONE , DVec2 ::ONE ) ) ;
let mut square = VectorDataTable ::new ( square ) ;
* square . one_instance_mut ( ) . transform * = DAffine2 ::from_angle ( core ::f64 ::consts ::FRAC_PI_4 ) ;
* square . get_mut ( 0 ) . unwrap ( ) . transform * = DAffine2 ::from_angle ( core ::f64 ::consts ::FRAC_PI_4 ) ;
let bounding_box = BoundingBoxNode {
vector_data : FutureWrapperNode ( square ) ,
}
@@ -2044,7 +2058,7 @@ mod test {
let vector_data = VectorData ::from_subpath ( source ) ;
let mut vector_data_table = VectorDataTable ::new ( vector_data . clone ( ) ) ;
* vector_data_table . one_instance_mut ( ) . transform = DAffine2 ::from_scale_angle_translation ( DVec2 ::splat ( 10. ) , 1. , DVec2 ::new ( 99. , 77. ) ) ;
* vector_data_table . get_mut ( 0 ) . unwrap ( ) . transform = DAffine2 ::from_scale_angle_translation ( DVec2 ::splat ( 10. ) , 1. , DVec2 ::new ( 99. , 77. ) ) ;
let beveled = super ::bevel ( ( ) , VectorDataTable ::new ( vector_data ) , 5. ) ;
let beveled = beveled . instance_ref_iter ( ) . next ( ) . unwrap ( ) . instance ;