@@ -134,8 +134,10 @@ public function testArcToBezierCurvesMatchesExpectedSamples(
134134 }
135135
136136 #[DataProvider('provideSegmentCountAndEndpointScenarios ' )]
137- public function testArcToBezierCurvesGeneratesExpectedSegmentsAndEndsAtTarget (array $ input , int $ expectedSegmentCount ): void
138- {
137+ public function testArcToBezierCurvesGeneratesExpectedSegmentsAndEndsAtTarget (
138+ array $ input ,
139+ int $ expectedSegmentCount ,
140+ ): void {
139141 $ converter = new SvgArcConverter ();
140142
141143 $ curves = $ converter ->arcToBezierCurves (
@@ -239,7 +241,22 @@ public static function provideNonDegenerateRadiusBoundaryScenarios(): iterable
239241 }
240242
241243 /**
242- * @return iterable<string, array{input: array{fromX: float, fromY: float, radiusX: float, radiusY: float, rotation: float, largeArc: int, sweep: int, toX: float, toY: float}, expectedSegmentCount: int, firstCurveExpected: array<int, float>, lastCurveExpected: array<int, float>}>
244+ * @return iterable<string, array{
245+ * input: array{
246+ * fromX: float,
247+ * fromY: float,
248+ * radiusX: float,
249+ * radiusY: float,
250+ * rotation: float,
251+ * largeArc: int,
252+ * sweep: int,
253+ * toX: float,
254+ * toY: float
255+ * },
256+ * expectedSegmentCount: int,
257+ * firstCurveExpected: array<int, float>,
258+ * lastCurveExpected: array<int, float>
259+ * }>
243260 */
244261 public static function provideExpectedCurveSamples (): iterable
245262 {
@@ -304,14 +321,8 @@ public static function provideExpectedCurveSamples(): iterable
304321 -3.67394039744206E-15 ,
305322 ],
306323 ];
307- }
308324
309- /**
310- * @return iterable<string, array{input: array{fromX: float, fromY: float, radiusX: float, radiusY: float, rotation: float, largeArc: int, sweep: int, toX: float, toY: float}, expectedSegmentCount: int}>
311- */
312- public static function provideSegmentCountAndEndpointScenarios (): iterable
313- {
314- yield 'quarter-like path with large-arc sweep yields two segments ' => [
325+ yield 'quarter-arc flag variant sample ' => [
315326 'input ' => [
316327 'fromX ' => 10.0 ,
317328 'fromY ' => 0.0 ,
@@ -324,19 +335,100 @@ public static function provideSegmentCountAndEndpointScenarios(): iterable
324335 'toY ' => 10.0 ,
325336 ],
326337 'expectedSegmentCount ' => 2 ,
338+ 'firstCurveExpected ' => [
339+ 20.95014085253355 ,
340+ 6.808005228802601 ,
341+ 20.95014085253355 ,
342+ 13.191994771197399 ,
343+ 17.071067811865476 ,
344+ 17.071067811865476 ,
345+ ],
346+ 'lastCurveExpected ' => [
347+ 13.1919947711974 ,
348+ 20.95014085253355 ,
349+ 6.808005228802601 ,
350+ 20.95014085253355 ,
351+ 2.9289321881345254 ,
352+ 17.071067811865476 ,
353+ ],
327354 ];
328355
329- yield 'clockwise path with opposite sweep still produces two segments ' => [
356+ yield 'asymmetric rotated normalized sample ' => [
330357 'input ' => [
331- 'fromX ' => 10.0 ,
332- 'fromY ' => 0.0 ,
358+ 'fromX ' => 0.0 ,
359+ 'fromY ' => 10.0 ,
360+ 'radiusX ' => 15.0 ,
361+ 'radiusY ' => 8.0 ,
362+ 'rotation ' => 45.0 ,
363+ 'largeArc ' => 1 ,
364+ 'sweep ' => 1 ,
365+ 'toX ' => 60.0 ,
366+ 'toY ' => 30.0 ,
367+ ],
368+ 'expectedSegmentCount ' => 2 ,
369+ 'firstCurveExpected ' => [
370+ -4.434385553963552 ,
371+ -6.137506191228175 ,
372+ 5.459153479092354 ,
373+ -14.902504650171249 ,
374+ 21.916666589738256 ,
375+ -9.416666946622618 ,
376+ ],
377+ 'lastCurveExpected ' => [
378+ 38.37417970038416 ,
379+ -3.930829243073987 ,
380+ 55.56561444603643 ,
381+ 13.862493808771795 ,
382+ 59.999999923071584 ,
383+ 29.99999972004403 ,
384+ ],
385+ ];
386+ }
387+
388+ /**
389+ * @return iterable<string, array{
390+ * input: array{
391+ * fromX: float,
392+ * fromY: float,
393+ * radiusX: float,
394+ * radiusY: float,
395+ * rotation: float,
396+ * largeArc: int,
397+ * sweep: int,
398+ * toX: float,
399+ * toY: float
400+ * },
401+ * expectedSegmentCount: int
402+ * }>
403+ */
404+ public static function provideSegmentCountAndEndpointScenarios (): iterable
405+ {
406+ yield 'half ellipse baseline remains two segments and reaches target ' => [
407+ 'input ' => [
408+ 'fromX ' => 0.0 ,
409+ 'fromY ' => 5.0 ,
333410 'radiusX ' => 10.0 ,
334- 'radiusY ' => 10 .0 ,
411+ 'radiusY ' => 5 .0 ,
335412 'rotation ' => 0.0 ,
336413 'largeArc ' => 0 ,
337- 'sweep ' => 0 ,
338- 'toX ' => 0.0 ,
339- 'toY ' => 10.0 ,
414+ 'sweep ' => 1 ,
415+ 'toX ' => 20.0 ,
416+ 'toY ' => 5.0 ,
417+ ],
418+ 'expectedSegmentCount ' => 2 ,
419+ ];
420+
421+ yield 'normalized radii scenario still reaches requested endpoint ' => [
422+ 'input ' => [
423+ 'fromX ' => 0.0 ,
424+ 'fromY ' => 0.0 ,
425+ 'radiusX ' => 5.0 ,
426+ 'radiusY ' => 5.0 ,
427+ 'rotation ' => 0.0 ,
428+ 'largeArc ' => 0 ,
429+ 'sweep ' => 1 ,
430+ 'toX ' => 30.0 ,
431+ 'toY ' => 0.0 ,
340432 ],
341433 'expectedSegmentCount ' => 2 ,
342434 ];
0 commit comments