|
| 1 | +<!DOCTYPE html> |
| 2 | +<html> |
| 3 | +<head> |
| 4 | +<meta charset="utf-8"> |
| 5 | +<title>SVG Path Data: Elliptical arc (A/a)</title> |
| 6 | +<link rel="help" href="https://svgwg.org/svg2-draft/paths.html#PathDataBNF"> |
| 7 | +<script src="/resources/testharness.js"></script> |
| 8 | +<script src="/resources/testharnessreport.js"></script> |
| 9 | +</head> |
| 10 | +<body> |
| 11 | + |
| 12 | +<svg id="svg" width="400" height="400"> |
| 13 | + <!-- Absolute arc --> |
| 14 | + <path id="test-A" d="M 100,100 A 50,50 0 0,1 200,100" fill="none" stroke="black" stroke-width="2"/> |
| 15 | + |
| 16 | + <!-- Relative arc --> |
| 17 | + <path id="test-a" d="M 100,150 a 50,50 0 0,1 100,0" fill="none" stroke="blue" stroke-width="2"/> |
| 18 | +</svg> |
| 19 | + |
| 20 | +<script> |
| 21 | +test(function() { |
| 22 | + const path = document.getElementById('test-A'); |
| 23 | + const length = path.getTotalLength(); |
| 24 | + |
| 25 | + assert_greater_than(length, 0, "A command creates an arc"); |
| 26 | + assert_greater_than(length, 100, "Arc is longer than straight line"); |
| 27 | +}, "Absolute arc: A 50,50 0 0,1 200,100"); |
| 28 | + |
| 29 | +test(function() { |
| 30 | + const pathA = document.getElementById('test-A'); |
| 31 | + const patha = document.getElementById('test-a'); |
| 32 | + |
| 33 | + // Both should create similar arcs |
| 34 | + assert_approx_equals(pathA.getTotalLength(), patha.getTotalLength(), 1, |
| 35 | + "Relative arc creates same curve as absolute"); |
| 36 | +}, "Relative arc: a 50,50 0 0,1 100,0 from (100,150)"); |
| 37 | + |
| 38 | +test(function() { |
| 39 | + const path = document.getElementById('test-A'); |
| 40 | + const endPoint = path.getPointAtLength(path.getTotalLength()); |
| 41 | + |
| 42 | + assert_approx_equals(endPoint.x, 200, 0.5, "Arc ends at specified x"); |
| 43 | + assert_approx_equals(endPoint.y, 100, 0.5, "Arc ends at specified y"); |
| 44 | +}, "Arc endpoint is correct"); |
| 45 | + |
| 46 | +test(function() { |
| 47 | + // Test large-arc-flag = 1 |
| 48 | + const svg = document.getElementById('svg'); |
| 49 | + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 50 | + path.setAttribute('d', 'M 100,200 A 50,50 0 1,1 200,200'); |
| 51 | + svg.appendChild(path); |
| 52 | + |
| 53 | + const length = path.getTotalLength(); |
| 54 | + assert_greater_than(length, 100, "Large arc flag creates larger arc"); |
| 55 | +}, "Large arc flag: A 50,50 0 1,1 200,200"); |
| 56 | + |
| 57 | +test(function() { |
| 58 | + // Test sweep-flag variations |
| 59 | + const svg = document.getElementById('svg'); |
| 60 | + const path0 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 61 | + path0.setAttribute('d', 'M 100,250 A 50,50 0 0,0 200,250'); |
| 62 | + svg.appendChild(path0); |
| 63 | + |
| 64 | + const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 65 | + path1.setAttribute('d', 'M 100,250 A 50,50 0 0,1 200,250'); |
| 66 | + svg.appendChild(path1); |
| 67 | + |
| 68 | + // Both paths share the same start (100,250) and end (200,250) with r=50. |
| 69 | + // The chord length (100) equals the diameter (2×50), so both arcs are semicircles |
| 70 | + // with identical path lengths — comparing lengths would yield near-zero floating-point |
| 71 | + // noise instead of a meaningful difference. |
| 72 | + // |
| 73 | + // The arcs bow in opposite directions: |
| 74 | + // |
| 75 | + // (150, 200) ← midpoint of sweep=1 arc (bows up) |
| 76 | + // ● |
| 77 | + // ●─────────────● |
| 78 | + // (100,250) (200,250) |
| 79 | + // ● |
| 80 | + // (150, 300) ← midpoint of sweep=0 arc (bows down) |
| 81 | + // |
| 82 | + // Comparing the midpoint y-coordinates gives a ~100-unit difference on all platforms. |
| 83 | + const mid0 = path0.getPointAtLength(path0.getTotalLength() / 2); |
| 84 | + const mid1 = path1.getPointAtLength(path1.getTotalLength() / 2); |
| 85 | + assert_greater_than(Math.abs(mid0.y - mid1.y), 10, |
| 86 | + "Different sweep flags create different arcs"); |
| 87 | +}, "Sweep flag variations: 0 vs 1"); |
| 88 | + |
| 89 | +test(function() { |
| 90 | + // Test x-axis-rotation |
| 91 | + const svg = document.getElementById('svg'); |
| 92 | + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 93 | + path.setAttribute('d', 'M 100,300 A 50,25 45 0,1 200,300'); |
| 94 | + svg.appendChild(path); |
| 95 | + |
| 96 | + const length = path.getTotalLength(); |
| 97 | + assert_greater_than(length, 0, "Rotated elliptical arc works"); |
| 98 | +}, "X-axis rotation: A 50,25 45 0,1 200,300"); |
| 99 | + |
| 100 | +test(function() { |
| 101 | + // Multiple arc arguments |
| 102 | + const svg = document.getElementById('svg'); |
| 103 | + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 104 | + path.setAttribute('d', 'M 50,350 A 25,25 0 0,1 100,350 25,25 0 0,1 150,350'); |
| 105 | + svg.appendChild(path); |
| 106 | + |
| 107 | + const endPoint = path.getPointAtLength(path.getTotalLength()); |
| 108 | + assert_approx_equals(endPoint.x, 150, 0.5, "Multiple arcs end at last point"); |
| 109 | +}, "Multiple arc arguments"); |
| 110 | + |
| 111 | +test(function() { |
| 112 | + // Flags without comma |
| 113 | + const svg = document.getElementById('svg'); |
| 114 | + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 115 | + path.setAttribute('d', 'M 200,100 A 50,50 0 01 300,100'); |
| 116 | + svg.appendChild(path); |
| 117 | + |
| 118 | + const length = path.getTotalLength(); |
| 119 | + assert_greater_than(length, 0, "Flags without comma separator work"); |
| 120 | +}, "Arc flags without comma: 01 instead of 0,1"); |
| 121 | + |
| 122 | +test(function() { |
| 123 | + // Flags with spaces |
| 124 | + const svg = document.getElementById('svg'); |
| 125 | + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 126 | + path.setAttribute('d', 'M 200,150 A 50,50 0 0 1 300,150'); |
| 127 | + svg.appendChild(path); |
| 128 | + |
| 129 | + const length = path.getTotalLength(); |
| 130 | + assert_greater_than(length, 0, "Flags with space separator work"); |
| 131 | +}, "Arc flags with spaces: 0 1 instead of 0,1"); |
| 132 | + |
| 133 | +test(function() { |
| 134 | + // Different radii (ellipse) |
| 135 | + const svg = document.getElementById('svg'); |
| 136 | + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 137 | + path.setAttribute('d', 'M 200,200 A 75,25 0 0,1 300,200'); |
| 138 | + svg.appendChild(path); |
| 139 | + |
| 140 | + const length = path.getTotalLength(); |
| 141 | + assert_greater_than(length, 0, "Elliptical arc with different radii works"); |
| 142 | +}, "Elliptical arc: A 75,25 (different rx and ry)"); |
| 143 | + |
| 144 | +test(function() { |
| 145 | + // Zero radius (should create line) |
| 146 | + const svg = document.getElementById('svg'); |
| 147 | + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 148 | + path.setAttribute('d', 'M 200,250 A 0,0 0 0,1 300,250'); |
| 149 | + svg.appendChild(path); |
| 150 | + |
| 151 | + const length = path.getTotalLength(); |
| 152 | + assert_approx_equals(length, 100, 0.5, "Zero radius creates straight line"); |
| 153 | +}, "Arc with zero radius becomes line"); |
| 154 | + |
| 155 | +test(function() { |
| 156 | + // Negative radius (should use absolute value) |
| 157 | + const svg = document.getElementById('svg'); |
| 158 | + const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 159 | + path1.setAttribute('d', 'M 200,300 A -50,50 0 0,1 300,300'); |
| 160 | + svg.appendChild(path1); |
| 161 | + |
| 162 | + const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| 163 | + path2.setAttribute('d', 'M 200,300 A 50,50 0 0,1 300,300'); |
| 164 | + svg.appendChild(path2); |
| 165 | + |
| 166 | + // Negative radius should be treated as absolute value |
| 167 | + assert_approx_equals(path1.getTotalLength(), path2.getTotalLength(), 0.5, |
| 168 | + "Negative radius treated as positive"); |
| 169 | +}, "Arc with negative radius uses absolute value"); |
| 170 | +</script> |
| 171 | +</body> |
| 172 | +</html> |
0 commit comments