@@ -11,7 +11,7 @@ use graphic_types::Vector;
1111use graphic_types:: raster_types:: { CPU , GPU , Raster } ;
1212use graphic_types:: { Graphic , IntoGraphicTable } ;
1313use kurbo:: simplify:: { SimplifyOptions , simplify_bezpath} ;
14- use kurbo:: { Affine , BezPath , DEFAULT_ACCURACY , Line , ParamCurve , PathEl , PathSeg , Shape } ;
14+ use kurbo:: { Affine , BezPath , DEFAULT_ACCURACY , Line , ParamCurve , ParamCurveArclen , PathEl , PathSeg , Shape } ;
1515use rand:: { Rng , SeedableRng } ;
1616use std:: collections:: hash_map:: DefaultHasher ;
1717use vector_types:: subpath:: { BezierHandles , ManipulatorGroup } ;
@@ -2187,11 +2187,13 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
21872187 // Lerp styles
21882188 let vector_alpha_blending = source_row. alpha_blending . lerp ( target_row. alpha_blending , time as f32 ) ;
21892189
2190- // Evaluate the spatial position on the control path for the translation component
2190+ // Evaluate the spatial position on the control path for the translation component.
2191+ // When the segment has zero arc length (e.g., two objects at the same position), inv_arclen
2192+ // produces NaN (0/0), so we fall back to the segment start point to avoid NaN translation.
21912193 let path_position = {
2192- // Use the original path segment index (not the clamped object index)
21932194 let segment_index = path_segment_index. min ( segment_count - 1 ) ;
21942195 let segment = control_bezpath. get_seg ( segment_index + 1 ) . unwrap ( ) ;
2196+ let parametric_t = if segment. arclen ( DEFAULT_ACCURACY ) < f64:: EPSILON { 0. } else { parametric_t } ;
21952197 let point = segment. eval ( parametric_t) ;
21962198 DVec2 :: new ( point. x , point. y )
21972199 } ;
@@ -2223,9 +2225,14 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
22232225 // the row transform (which will be group_transform * lerped_transform after the
22242226 // pipeline's Transform node runs), the lerped_transform cancels out and children
22252227 // get the correct footprint: parent * group_transform * child_transform.
2226- let lerped_inverse = lerped_transform. inverse ( ) ;
2227- for row in graphic_table_content. iter_mut ( ) {
2228- * row. transform = lerped_inverse * * row. transform ;
2228+ // Only pre-compensate if the lerped transform is invertible (non-zero determinant).
2229+ // A zero determinant can occur when interpolated scale passes through zero (e.g., flipped axes),
2230+ // in which case we skip pre-compensation to avoid propagating NaN through upstream_data transforms.
2231+ if lerped_transform. matrix2 . determinant ( ) . abs ( ) > f64:: EPSILON {
2232+ let lerped_inverse = lerped_transform. inverse ( ) ;
2233+ for row in graphic_table_content. iter_mut ( ) {
2234+ * row. transform = lerped_inverse * * row. transform ;
2235+ }
22292236 }
22302237
22312238 let mut vector = Vector {
0 commit comments