@@ -2151,68 +2151,72 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
21512151 let subpath_anchors = anchor_count ( control_bezpath) ;
21522152 let max_content_index = content. len ( ) . saturating_sub ( 1 ) ;
21532153
2154- // Compute per-segment arc lengths for spatial positioning along the control path
2155- let segment_lengths: Vec < f64 > = control_bezpath. segments ( ) . map ( |seg| seg. perimeter ( DEFAULT_ACCURACY ) ) . collect ( ) ;
2156-
2157- // Compute segment weights based on the user's chosen spacing metric
2158- let segment_weights: Vec < f64 > = match distribution {
2159- InterpolationDistribution :: Objects => vec ! [ 1. ; segment_count] ,
2160- InterpolationDistribution :: Distances => segment_lengths. clone ( ) ,
2161- InterpolationDistribution :: Angles | InterpolationDistribution :: Sizes | InterpolationDistribution :: Slants => ( 0 ..segment_count)
2162- . map ( |i| {
2163- let source_index = ( content_offset + i) . min ( max_content_index) ;
2164- let target_index = if is_closed && i >= subpath_anchors - 1 {
2165- content_offset
2166- } else {
2167- ( content_offset + i + 1 ) . min ( max_content_index)
2168- } ;
2154+ // Map the fractional progression to a segment index and local blend time using the chosen weights.
2155+ let ( local_source_index, time) = if fractional_progression >= 1. {
2156+ ( segment_count - 1 , 1. )
2157+ } else if matches ! ( distribution, InterpolationDistribution :: Objects ) {
2158+ // Fast path for uniform distribution: direct index calculation without allocation or iteration
2159+ let scaled = fractional_progression * segment_count as f64 ;
2160+ let index = ( scaled. ceil ( ) as usize ) . saturating_sub ( 1 ) ;
2161+ ( index, scaled - index as f64 )
2162+ } else {
2163+ // Compute segment weights based on the user's chosen spacing metric
2164+ let segment_weights: Vec < f64 > = match distribution {
2165+ InterpolationDistribution :: Objects => unreachable ! ( ) ,
2166+ InterpolationDistribution :: Distances => control_bezpath. segments ( ) . map ( |seg| seg. perimeter ( DEFAULT_ACCURACY ) ) . collect ( ) ,
2167+ InterpolationDistribution :: Angles | InterpolationDistribution :: Sizes | InterpolationDistribution :: Slants => ( 0 ..segment_count)
2168+ . map ( |i| {
2169+ let source_index = ( content_offset + i) . min ( max_content_index) ;
2170+ let target_index = if is_closed && i >= subpath_anchors - 1 {
2171+ content_offset
2172+ } else {
2173+ ( content_offset + i + 1 ) . min ( max_content_index)
2174+ } ;
21692175
2170- let ( Some ( source) , Some ( target) ) = ( content. get ( source_index) , content. get ( target_index) ) else {
2171- return 0. ;
2172- } ;
2173- let ( s_angle, s_scale, s_skew) = source. transform . decompose_rotation_scale_skew ( ) ;
2174- let ( t_angle, t_scale, t_skew) = target. transform . decompose_rotation_scale_skew ( ) ;
2175-
2176- match distribution {
2177- InterpolationDistribution :: Angles => {
2178- let mut diff = t_angle - s_angle;
2179- if diff > PI {
2180- diff -= TAU ;
2181- } else if diff < -PI {
2182- diff += TAU ;
2176+ let ( Some ( source) , Some ( target) ) = ( content. get ( source_index) , content. get ( target_index) ) else {
2177+ return 0. ;
2178+ } ;
2179+ let ( s_angle, s_scale, s_skew) = source. transform . decompose_rotation_scale_skew ( ) ;
2180+ let ( t_angle, t_scale, t_skew) = target. transform . decompose_rotation_scale_skew ( ) ;
2181+
2182+ match distribution {
2183+ InterpolationDistribution :: Angles => {
2184+ let mut diff = t_angle - s_angle;
2185+ if diff > PI {
2186+ diff -= TAU ;
2187+ } else if diff < -PI {
2188+ diff += TAU ;
2189+ }
2190+ diff. abs ( )
21832191 }
2184- diff. abs ( )
2192+ InterpolationDistribution :: Sizes => ( t_scale - s_scale) . length ( ) ,
2193+ InterpolationDistribution :: Slants => ( t_skew. atan ( ) - s_skew. atan ( ) ) . abs ( ) ,
2194+ _ => unreachable ! ( ) ,
21852195 }
2186- InterpolationDistribution :: Sizes => ( t_scale - s_scale) . length ( ) ,
2187- InterpolationDistribution :: Slants => ( t_skew. atan ( ) - s_skew. atan ( ) ) . abs ( ) ,
2188- _ => unreachable ! ( ) ,
2189- }
2190- } )
2191- . collect ( ) ,
2192- } ;
2196+ } )
2197+ . collect ( ) ,
2198+ } ;
21932199
2194- let total_weight: f64 = segment_weights. iter ( ) . sum ( ) ;
2200+ let total_weight: f64 = segment_weights. iter ( ) . sum ( ) ;
21952201
2196- // Map the fractional progression to a segment index and local blend time using the chosen weights.
2197- // When all weights are zero (all elements identical in the chosen metric), there's zero interval to traverse.
2198- let ( local_source_index, time) = if total_weight <= f64:: EPSILON {
2199- ( 0 , 0. )
2200- } else if fractional_progression >= 1. {
2201- ( segment_count - 1 , 1. )
2202- } else {
2203- let mut accumulator = 0. ;
2204- let mut found_index = segment_count - 1 ;
2205- let mut found_t = 1. ;
2206- for ( i, weight) in segment_weights. iter ( ) . enumerate ( ) {
2207- let ratio = weight / total_weight;
2208- if fractional_progression <= accumulator + ratio {
2209- found_index = i;
2210- found_t = if ratio > f64:: EPSILON { ( fractional_progression - accumulator) / ratio } else { 0. } ;
2211- break ;
2202+ // When all weights are zero (all elements identical in the chosen metric), there's zero interval to traverse.
2203+ if total_weight <= f64:: EPSILON {
2204+ ( 0 , 0. )
2205+ } else {
2206+ let mut accumulator = 0. ;
2207+ let mut found_index = segment_count - 1 ;
2208+ let mut found_t = 1. ;
2209+ for ( i, weight) in segment_weights. iter ( ) . enumerate ( ) {
2210+ let ratio = weight / total_weight;
2211+ if fractional_progression <= accumulator + ratio {
2212+ found_index = i;
2213+ found_t = if ratio > f64:: EPSILON { ( fractional_progression - accumulator) / ratio } else { 0. } ;
2214+ break ;
2215+ }
2216+ accumulator += ratio;
22122217 }
2213- accumulator += ratio ;
2218+ ( found_index , found_t )
22142219 }
2215- ( found_index, found_t)
22162220 } ;
22172221
22182222 // Convert the blend time to a parametric t for evaluating spatial position on the control path
@@ -2237,11 +2241,6 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
22372241 let source_index = source_index. min ( max_content_index) ;
22382242 let target_index = target_index. min ( max_content_index) ;
22392243
2240- // At the end of an open subpath with no more interpolation needed, return the final element
2241- if !is_closed && time >= 1. && local_source_index >= subpath_anchors - 2 {
2242- return content. into_iter ( ) . nth ( target_index) . into_iter ( ) . collect ( ) ;
2243- }
2244-
22452244 // Use indexed access to borrow only the two rows we need, avoiding collecting the entire table
22462245 let ( Some ( source_row) , Some ( target_row) ) = ( content. get ( source_index) , content. get ( target_index) ) else {
22472246 return content;
@@ -2298,16 +2297,17 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
22982297 }
22992298 }
23002299
2301- // Fast path: when exactly at the source object, clone its geometry directly instead of
2302- // extracting manipulator groups, subdividing, interpolating, and rebuilding the vector.
2303- if time == 0. {
2304- let mut vector = source_row. element . clone ( ) ;
2305- vector. upstream_data = Some ( graphic_table_content) ;
2306-
2300+ // Fast path: when exactly at either endpoint, clone the corresponding geometry directly
2301+ // instead of extracting manipulator groups, subdividing, interpolating, and rebuilding.
2302+ if time == 0. || time == 1. {
2303+ let row = if time == 0. { source_row } else { target_row } ;
23072304 return Table :: new_from_row ( TableRow {
2308- element : vector,
2305+ element : Vector {
2306+ upstream_data : Some ( graphic_table_content) ,
2307+ ..row. element . clone ( )
2308+ } ,
2309+ alpha_blending : * row. alpha_blending ,
23092310 transform : lerped_transform,
2310- alpha_blending : * source_row. alpha_blending ,
23112311 ..Default :: default ( )
23122312 } ) ;
23132313 }
0 commit comments