apexcharts
Version:
A JavaScript Chart Library
132 lines (118 loc) • 4.34 kB
JavaScript
// @ts-check
/**
* Shared donut / pie slice arc-path geometry.
*
* Both Pie (`getRoundedSlicePath`) and Sunburst (`_arcPath`) build the same
* rounded donut segment: outer + inner arcs joined by quadratic-Bezier fillets
* at the corners. These were two independent copies that could drift; this
* module is the single source. Each caller keeps its own radius/spacing
* clamping and passes the already-resolved geometry in, so the emitted path
* strings are byte-identical to the previous inline copies.
*
* Angle convention: degrees, 0deg = 12 o'clock, increasing clockwise.
* `spanDeg` is passed explicitly (not recomputed from `a1 - a0`) so the
* large-arc flags match each caller's original float arithmetic exactly.
*
* @module charts/common/arc/ArcPath
*/
const D2R = Math.PI / 180
const R2D = 180 / Math.PI
/**
* Point on a circle of radius `radius` at `deg`, relative to center (cx, cy).
* @param {number} cx @param {number} cy @param {number} radius @param {number} deg
* @returns {{x: number, y: number}}
*/
export function arcPoint(cx, cy, radius, deg) {
return {
x: cx + radius * Math.cos((deg - 90) * D2R),
y: cy + radius * Math.sin((deg - 90) * D2R),
}
}
/** @param {{x: number, y: number}} p */
const xy = (p) => `${p.x} ${p.y}`
/**
* Rounded donut segment (inner radius rIn > 0): all four corners filleted.
* @param {{cx: number, cy: number, rIn: number, rOut: number, a0: number, a1: number, r: number, spanDeg: number}} o
* @returns {string}
*/
export function roundedDonutSegmentPath({ cx, cy, rIn, rOut, a0, a1, r, spanDeg }) {
/** @param {number} radius @param {number} deg */
const ptAt = (radius, deg) => arcPoint(cx, cy, radius, deg)
const degOut = (r / rOut) * R2D
const degIn = (r / rIn) * R2D
const oStart = ptAt(rOut, a0 + degOut)
const oEnd = ptAt(rOut, a1 - degOut)
const largeOut = spanDeg - 2 * degOut > 180 ? 1 : 0
const ocEnd = ptAt(rOut, a1)
const rEndOut = ptAt(rOut - r, a1)
const ocStart = ptAt(rOut, a0)
const rStartOut = ptAt(rOut - r, a0)
const iEnd = ptAt(rIn, a1 - degIn)
const iStart = ptAt(rIn, a0 + degIn)
const largeIn = spanDeg - 2 * degIn > 180 ? 1 : 0
const icEnd = ptAt(rIn, a1)
const rEndIn = ptAt(rIn + r, a1)
const icStart = ptAt(rIn, a0)
const rStartIn = ptAt(rIn + r, a0)
return [
'M', xy(oStart),
'A', rOut, rOut, 0, largeOut, 1, xy(oEnd),
'Q', xy(ocEnd), xy(rEndOut),
'L', xy(rEndIn),
'Q', xy(icEnd), xy(iEnd),
'A', rIn, rIn, 0, largeIn, 0, xy(iStart),
'Q', xy(icStart), xy(rStartIn),
'L', xy(rStartOut),
'Q', xy(ocStart), xy(oStart),
'Z',
].join(' ')
}
/**
* Rounded pie / polarArea segment (solid): the two outer corners are filleted,
* the center apex stays sharp.
* @param {{cx: number, cy: number, rOut: number, a0: number, a1: number, r: number, spanDeg: number}} o
* @returns {string}
*/
export function roundedPieSegmentPath({ cx, cy, rOut, a0, a1, r, spanDeg }) {
/** @param {number} radius @param {number} deg */
const ptAt = (radius, deg) => arcPoint(cx, cy, radius, deg)
const degOut = (r / rOut) * R2D
const oStart = ptAt(rOut, a0 + degOut)
const oEnd = ptAt(rOut, a1 - degOut)
const largeOut = spanDeg - 2 * degOut > 180 ? 1 : 0
const ocEnd = ptAt(rOut, a1)
const rEndOut = ptAt(rOut - r, a1)
const ocStart = ptAt(rOut, a0)
const rStartOut = ptAt(rOut - r, a0)
return [
'M', xy(oStart),
'A', rOut, rOut, 0, largeOut, 1, xy(oEnd),
'Q', xy(ocEnd), xy(rEndOut),
'L', `${cx} ${cy}`,
'L', xy(rStartOut),
'Q', xy(ocStart), xy(oStart),
'Z',
].join(' ')
}
/**
* Sharp (unrounded) donut segment. Used when a segment is too thin/narrow to
* round meaningfully.
* @param {{cx: number, cy: number, rIn: number, rOut: number, a0: number, a1: number, spanDeg: number}} o
* @returns {string}
*/
export function sharpDonutSegmentPath({ cx, cy, rIn, rOut, a0, a1, spanDeg }) {
/** @param {number} radius @param {number} deg */
const ptAt = (radius, deg) => arcPoint(cx, cy, radius, deg)
const largeArc = spanDeg > 180 ? 1 : 0
const A = ptAt(rOut, a0)
const B = ptAt(rOut, a1)
const C = ptAt(rIn, a1)
const Din = ptAt(rIn, a0)
return [
'M', xy(A),
'A', rOut, rOut, 0, largeArc, 1, xy(B),
'L', xy(C),
'A', rIn, rIn, 0, largeArc, 0, xy(Din),
'Z',
].join(' ')
}