* Path the arc, respecting border radius by separating into left and right halves. * * Start End * * 1--->a--->2 Outer * / \ * 8 3 * | | * | | * 7 4 * \ / * 6<---b<---5 Inner
( ctx: CanvasRenderingContext2D, element: ArcElement, offset: number, spacing: number, end: number, circular: boolean, )
| 114 | * 6<---b<---5 Inner |
| 115 | */ |
| 116 | function pathArc( |
| 117 | ctx: CanvasRenderingContext2D, |
| 118 | element: ArcElement, |
| 119 | offset: number, |
| 120 | spacing: number, |
| 121 | end: number, |
| 122 | circular: boolean, |
| 123 | ) { |
| 124 | const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element; |
| 125 | |
| 126 | const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0); |
| 127 | const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0; |
| 128 | |
| 129 | let spacingOffset = 0; |
| 130 | const alpha = end - start; |
| 131 | |
| 132 | if (spacing) { |
| 133 | // When spacing is present, it is the same for all items |
| 134 | // So we adjust the start and end angle of the arc such that |
| 135 | // the distance is the same as it would be without the spacing |
| 136 | const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0; |
| 137 | const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0; |
| 138 | const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2; |
| 139 | const adjustedAngle = avNogSpacingRadius !== 0 ? (alpha * avNogSpacingRadius) / (avNogSpacingRadius + spacing) : alpha; |
| 140 | spacingOffset = (alpha - adjustedAngle) / 2; |
| 141 | } |
| 142 | |
| 143 | const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius; |
| 144 | const angleOffset = (alpha - beta) / 2; |
| 145 | const startAngle = start + angleOffset + spacingOffset; |
| 146 | const endAngle = end - angleOffset - spacingOffset; |
| 147 | const {outerStart, outerEnd, innerStart, innerEnd} = parseBorderRadius(element, innerRadius, outerRadius, endAngle - startAngle); |
| 148 | |
| 149 | const outerStartAdjustedRadius = outerRadius - outerStart; |
| 150 | const outerEndAdjustedRadius = outerRadius - outerEnd; |
| 151 | const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius; |
| 152 | const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius; |
| 153 | |
| 154 | const innerStartAdjustedRadius = innerRadius + innerStart; |
| 155 | const innerEndAdjustedRadius = innerRadius + innerEnd; |
| 156 | const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius; |
| 157 | const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius; |
| 158 | |
| 159 | ctx.beginPath(); |
| 160 | |
| 161 | if (circular) { |
| 162 | // The first arc segments from point 1 to point a to point 2 |
| 163 | const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2; |
| 164 | ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle); |
| 165 | ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle); |
| 166 | |
| 167 | // The corner segment from point 2 to point 3 |
| 168 | if (outerEnd > 0) { |
| 169 | const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y); |
| 170 | ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI); |
| 171 | } |
| 172 | |
| 173 | // The line from point 3 to point 4 |
no test coverage detected