svg-pathdata
Version:
Manipulate SVG path data (path[d] attribute content) simply and efficiently.
198 lines (165 loc) • 9.72 kB
text/typescript
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
// Sample pathes from MDN
// https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
// Here we have to round output before testing since there is some lil
// differences across browsers.
function testArcToCurve(input: string) {
const data = new SVGPathData(input).aToC();
return data.round().encode();
}
describe('Converting elliptical arc commands to curves', () => {
test('should work sweepFlag on 0 and largeArcFlag on 0', () => {
// Absolute
expect(
testArcToCurve('M80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 Z'),
).toEqual(
'M80 80C80 104.8528137423857 100.1471862576143 125 125 125L125 80z',
);
// Relative
expect(
testArcToCurve('M80 80 a 45 45, 0, 0, 0, 125 125 L 125 80 Z'),
).toEqual(
'M80 80c-34.5177968644246 34.5177968644246 -34.5177968644246 90.4822031355754 0 125c34.5177968644246 34.5177968644246 90.4822031355754 34.5177968644246 125 0L125 80z',
);
});
test('should work sweepFlag on 1 and largeArcFlag on 0', () => {
// Absolute
expect(
testArcToCurve('M230 80 A 45 45, 0, 1, 0, 275 125 L 275 80 Z'),
).toEqual(
'M230 80C205.1471862576143 80 185 100.1471862576143 185 125C185 149.8528137423857 205.1471862576143 170 230 170C254.8528137423857 170 275 149.8528137423857 275 125L275 80z',
);
// Relative
expect(testArcToCurve('M230 80 a 45 45 0 1 0 45 45')).toEqual(
'M230 80c-24.8528137423857 0 -45 20.1471862576143 -45 45c0 24.8528137423857 20.1471862576143 45 45 45c24.8528137423857 0 45 -20.1471862576143 45 -45',
);
});
test('should work sweepFlag on 0 and largeArcFlag on 1', () => {
// Absolute
expect(
testArcToCurve('M80 230 A 45 45, 0, 0, 1, 125 275 L 125 230 Z'),
).toEqual(
'M80 230C104.8528137423857 230 125 250.1471862576143 125 275L125 230z',
);
// Relative
expect(testArcToCurve('M80 230 a 45 45 0 0 1 45 45')).toEqual(
'M80 230c24.8528137423857 0 45 20.1471862576143 45 45',
);
});
test('should work sweepFlag on 1 and largeArcFlag on 1', () => {
// Absolute
expect(
testArcToCurve('M230 230 A 45 45, 0, 1, 1, 275 275 L 275 230 Z'),
).toEqual(
'M230 230C230 205.1471862576143 250.1471862576143 185 275 185C299.8528137423857 185 320 205.1471862576143 320 230C320 254.8528137423857 299.8528137423857 275 275 275L275 230z',
);
// Relative
expect(testArcToCurve('M230 230 a 45 45 0 1 1 45 45')).toEqual(
'M230 230c0 -24.8528137423857 20.1471862576143 -45 45 -45c24.8528137423857 0 45 20.1471862576143 45 45c0 24.8528137423857 -20.1471862576143 45 -45 45',
);
});
test('should handle zero radius arcs by converting to lines', () => {
// Zero y-radius (absolute)
expect(testArcToCurve('M0 0A80 0 0 0 0 125 125')).toEqual(
'M0 0C41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125',
);
// Zero x-radius (absolute)
expect(testArcToCurve('M0 0A0 80 0 0 0 125 125')).toEqual(
'M0 0C41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125',
);
// Both x and y radius zero (absolute)
expect(testArcToCurve('M0 0A0 0 0 0 0 125 125')).toEqual(
'M0 0C41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125',
);
// Both x and y radius zero (relative)
expect(testArcToCurve('M0 0 a 0 80 0 0 0 125 125')).toEqual(
'M0 0c41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125',
);
});
test('should convert to correct arc', () => {
expect(testArcToCurve('M 30 30 A 30 30 90 0 0 30 80 Z')).toEqual(
'M30 30C21.6206442862417 35.5582357774914 16.583123951777 44.9447731434901 16.583123951777 55C16.583123951777 65.0552268565099 21.6206442862417 74.4417642225086 30 80z',
);
});
test('should correctly handle rotated arcs', () => {
// 45 degree rotation (absolute)
expect(testArcToCurve('M 50 50 A 30 15 45 0 1 100 100')).toEqual(
'M50 50C56.9035593728849 43.0964406271151 73.6928812542302 48.6928812542302 87.5 62.5C101.3071187457698 76.3071187457698 106.9035593728849 93.0964406271151 100 100',
);
// 90 degree rotation (absolute)
expect(testArcToCurve('M 30 30 A 30 15 90 0 1 80 80')).toEqual(
'M30 30C36.9035593728849 2.3857625084603 53.6928812542302 -8.8071187457698 67.5 5C81.3071187457698 18.8071187457698 86.9035593728849 52.3857625084603 80 80',
);
// 180 degree rotation (absolute, equivalent to flipping x and y radii)
expect(testArcToCurve('M 30 30 A 30 15 180 0 1 80 80')).toEqual(
'M30 30C57.6142374125551 23.0964408852183 91.1928806985313 28.6928815616988 104.999999309466 42.5000001726335C118.8071179204008 56.3071187835682 107.6142370311837 73.096440503847 80 80',
);
// 45 degree rotation (relative)
expect(testArcToCurve('M 50 50 a 30 15 45 0 1 50 50')).toEqual(
'M50 50c6.9035593728849 -6.9035593728849 23.6928812542302 -1.3071187457698 37.5 12.5c13.8071187457698 13.8071187457698 19.4035593728849 30.5964406271151 12.5 37.5',
);
// 90 degree rotation (relative)
expect(testArcToCurve('M 30 30 a 30 15 90 0 1 50 50')).toEqual(
'M30 30c6.9035593728849 -27.6142374915397 23.6928812542302 -38.8071187457698 37.5 -25c13.8071187457698 13.8071187457698 19.4035593728849 47.3857625084603 12.5 75',
);
});
test('should handle different radius combinations', () => {
// Very different radius values
expect(testArcToCurve('M 50 50 A 50 10 0 0 1 100 60')).toEqual(
'M50 50C77.6142374915397 50 100 54.4771525016921 100 60',
);
// Equal radius values (circle)
expect(testArcToCurve('M 50 50 A 30 30 0 0 1 100 80')).toEqual(
'M50 50C59.7206056797015 40.1416040718891 74.9250308199694 38.2155183741452 86.7968382872808 45.338602854532C98.6686457545922 52.4616873349189 104.1241819696502 66.7837498458583 100 80',
);
});
test('should handle full and nearly-full ellipses', () => {
// Nearly full ellipse with large-arc flag
expect(testArcToCurve('M 100 50 A 50 30 0 1 1 99 50')).toEqual(
'M100 50C127.5152793990511 50.1650999315973 149.636959976665 63.6387150592171 149.4993749804677 80.1485018375802C149.3617899842705 96.6582886159432 127.0166552662119 109.9984999624981 99.5 109.9984999624981C71.9833447337882 109.9984999624981 49.6382100157296 96.6582886159433 49.5006250195323 80.1485018375802C49.363040023335 63.6387150592171 71.4847206009489 50.1650999315973 99 50',
);
// Arc crossing coordinate axes
expect(testArcToCurve('M 0 0 A 50 50 0 0 1 100 0')).toEqual(
'M0 0C0 -27.6142374915397 22.3857625084603 -50 50 -50C77.6142374915397 -50 100 -27.6142374915397 100 0',
);
});
test('should correctly handle special cases with precise angles', () => {
// Vertical line with 90 degree rotation
expect(testArcToCurve('M 30 30 A 30 30 90 0 0 30 80')).toEqual(
'M30 30C21.6206442862417 35.5582357774914 16.583123951777 44.9447731434901 16.583123951777 55C16.583123951777 65.0552268565099 21.6206442862417 74.4417642225086 30 80',
);
// Same vertical line but in opposite direction (sweepFlag changed from 0 to 1)
expect(testArcToCurve('M 30 30 A 30 30 90 0 1 30 80')).toEqual(
'M30 30C38.3793557137583 35.5582357774914 43.416876048223 44.9447731434901 43.416876048223 55C43.416876048223 65.0552268565099 38.3793557137583 74.4417642225086 30 80',
);
// 90 degree arc segment
expect(testArcToCurve('M 50 50 A 30 30 0 0 1 80 80')).toEqual(
'M50 50C66.5685424949238 50 80 63.4314575050762 80 80',
);
});
test('should handle extreme values correctly', () => {
// Very small but non-zero radius
expect(testArcToCurve('M 50 50 A 0.1 0.1 0 0 1 51 51')).toEqual(
'M50 50C50.2761423749154 49.7238576250846 50.7238576250846 49.7238576250846 51 50C51.2761423749154 50.2761423749154 51.2761423749154 50.7238576250846 51 51',
);
// Very large radius
expect(testArcToCurve('M 0 0 A 1000 1000 0 0 1 10 10')).toEqual(
'M0 0C3.3568621862997 3.3097211449502 6.6902788550498 6.6431378137003 10 10',
);
});
test('should split arcs to mutiple curves', () => {
// Half circle (relative)
expect(testArcToCurve('M4.6 20 a 1.556 1.556 0 1 0 -2.2 -2.2z')).toEqual(
'M4.6 20c0.4073109040792 -0.3900354880021 0.5717037274325 -0.9699266164694 0.4296821520769 -1.5156918834261c-0.1420215753557 -0.5457652669567 -0.568225001694 -0.9719686932951 -1.1139902686508 -1.1139902686508c-0.5457652669567 -0.1420215753557 -1.125656395424 0.0223712479977 -1.5156918834261 0.4296821520769z',
);
// Almost full circle (absolute)
expect(testArcToCurve('M100 100 A50 50 0 1 1 101 100')).toEqual(
'M100 100C72.484720600949 99.7248334473379 50.363040023335 77.2688082346382 50.5006250195323 49.7524969373664C50.6382100157295 22.2361856400946 72.9833447337881 0.0025000625031 100.5 0.0025000625031C128.0166552662119 0.0025000625031 150.3617899842705 22.2361856400946 150.4993749804677 49.7524969373664C150.636959976665 77.2688082346382 128.5152793990511 99.7248334473379 101 100',
);
// Almost full circle (relative)
expect(testArcToCurve('M100 100 a50 50 0 1 1 1 0')).toEqual(
'M100 100c-27.515279399051 -0.2751665526621 -49.636959976665 -22.7311917653618 -49.4993749804677 -50.2475030626336c0.1375849961973 -27.5163112972718 22.4827197142558 -49.7499968748633 49.9993749804677 -49.7499968748633c27.5166552662119 0 49.8617899842705 22.2336855775915 49.9993749804677 49.7499968748633c0.1375849961973 27.5163112972718 -21.9840955814167 49.9723365099715 -49.4993749804677 50.2475030626336',
);
});
});