@@ -557,14 +557,19 @@ fn stroke_chain_arc(
557557 stroke_chain_point (path_ix , last , center + to_offset , transform );
558558 return ;
559559 }
560- let start_angle = atan2 (from_offset . y , from_offset . x );
561- let end_angle = atan2 (to_offset . y , to_offset . x );
562- let sweep = stroke_normalize_positive_angle (end_angle - start_angle );
560+ // WGSL atan2 is implementation-defined when x is zero, and axis-aligned caps produce
561+ // exactly those inputs. The sweep must come from atan2(cross, dot) and interior points
562+ // must rotate the start offset; absolute angles of the offsets are not computable here.
563+ let cross_ft = (from_offset . x * to_offset . y ) - (from_offset . y * to_offset . x );
564+ let sweep = stroke_normalize_positive_angle (atan2 (cross_ft , dot (from_offset , to_offset )));
563565 let n = stroke_arc_subdivision_count (radius , sweep , arc_detail_scale );
564566 let step = sweep / f32 (n + 1u );
567+ let rot_c = cos (step );
568+ let rot_s = sin (step );
569+ var offset = from_offset ;
565570 for (var i = 1u ; i <= n ; i += 1u ) {
566- let a = start_angle + ( step * f32 ( i ));
567- stroke_chain_point (path_ix , last , center + ( vec2 ( cos ( a ), sin ( a )) * radius ) , transform );
571+ offset = vec2 (( offset . x * rot_c ) - ( offset . y * rot_s ), ( offset . x * rot_s ) + ( offset . y * rot_c ));
572+ stroke_chain_point (path_ix , last , center + offset , transform );
568573 }
569574 stroke_chain_point (path_ix , last , center + to_offset , transform );
570575}
@@ -577,20 +582,22 @@ fn stroke_calc_arc(
577582 v1 : vec2f , o1 : vec2f , o2 : vec2f ,
578583 half_width : f32 , arc_detail_scale : f32 , transform : Transform ,
579584) {
580- var a1 = atan2 (o1 . y , o1 . x );
581- var a2 = atan2 (o2 . y , o2 . x );
585+ // WGSL atan2 is implementation-defined when x is zero, and axis-aligned joins produce
586+ // exactly those inputs. The sweep must come from atan2(cross, dot) and interior points
587+ // must rotate o1; absolute angles of the offsets are not computable here.
588+ let cross_oo = (o1 . x * o2 . y ) - (o1 . y * o2 . x );
589+ let sweep = stroke_normalize_positive_angle (atan2 (cross_oo , dot (o1 , o2 )));
582590 let da = acos (half_width / (half_width + (0 .125 / arc_detail_scale ))) * 2 .0 ;
583591 stroke_chain_point (path_ix , last , v1 + o1 , transform );
584- if a1 > a2 {
585- a2 += 6 .283185307179586 ;
586- }
587592 // Bounded for GPU safety; matches the CPU count for all real detail scales.
588- let n = clamp (i32 ((a2 - a1 ) / da ), 0 , 1024 );
589- let step = (a2 - a1 ) / f32 (n + 1 );
590- a1 += step ;
593+ let n = clamp (i32 (sweep / da ), 0 , 1024 );
594+ let step = sweep / f32 (n + 1 );
595+ let rot_c = cos (step );
596+ let rot_s = sin (step );
597+ var offset = o1 ;
591598 for (var i = 0 ; i < n ; i += 1 ) {
592- stroke_chain_point ( path_ix , last , v1 + ( vec2 ( cos ( a1 ), sin ( a1 )) * half_width ), transform );
593- a1 += step ;
599+ offset = vec2 (( offset . x * rot_c ) - ( offset . y * rot_s ), ( offset . x * rot_s ) + ( offset . y * rot_c ) );
600+ stroke_chain_point ( path_ix , last , v1 + offset , transform ) ;
594601 }
595602 stroke_chain_point (path_ix , last , v1 + o2 , transform );
596603}
0 commit comments