UNPKG

react-native-chart-kit

Version:

Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.

143 lines (142 loc) 5.25 kB
const fullCircle = Math.PI * 2; const defaultStartAngle = -Math.PI / 2; const clamp = (value, min, max) => { if (max < min) { return min; } return Math.min(Math.max(value, min), max); }; const isFinitePositive = (value) => Number.isFinite(value) && value > 0; const polarToCartesian = ({ angle, centerX, centerY, radius }) => ({ x: centerX + radius * Math.cos(angle), y: centerY + radius * Math.sin(angle) }); const formatNumber = (value) => Number.isInteger(value) ? String(value) : Number(value.toFixed(4)).toString(); const moveTo = (point) => `M ${formatNumber(point.x)} ${formatNumber(point.y)}`; const lineTo = (point) => `L ${formatNumber(point.x)} ${formatNumber(point.y)}`; const arcTo = ({ end, largeArc, radius, sweep }) => `A ${formatNumber(radius)} ${formatNumber(radius)} 0 ${largeArc} ${sweep} ${formatNumber(end.x)} ${formatNumber(end.y)}`; const getArcPath = ({ centerX, centerY, endAngle, innerRadius, radius, startAngle }) => { const span = Math.max(0, endAngle - startAngle); const isFullCircle = span >= fullCircle - 0.0001; const outerStart = polarToCartesian({ angle: startAngle, centerX, centerY, radius }); const outerEnd = polarToCartesian({ angle: isFullCircle ? startAngle + Math.PI : endAngle, centerX, centerY, radius }); const largeArc = span > Math.PI ? 1 : 0; if (innerRadius <= 0) { const center = { x: centerX, y: centerY }; if (isFullCircle) { return [ moveTo(center), lineTo(outerStart), arcTo({ end: outerEnd, largeArc: 1, radius, sweep: 1 }), arcTo({ end: outerStart, largeArc: 1, radius, sweep: 1 }), "Z" ].join(" "); } return [ moveTo(center), lineTo(outerStart), arcTo({ end: outerEnd, largeArc, radius, sweep: 1 }), "Z" ].join(" "); } const innerStart = polarToCartesian({ angle: startAngle, centerX, centerY, radius: innerRadius }); const innerEnd = polarToCartesian({ angle: isFullCircle ? startAngle + Math.PI : endAngle, centerX, centerY, radius: innerRadius }); if (isFullCircle) { return [ moveTo(outerStart), arcTo({ end: outerEnd, largeArc: 1, radius, sweep: 1 }), arcTo({ end: outerStart, largeArc: 1, radius, sweep: 1 }), lineTo(innerStart), arcTo({ end: innerEnd, largeArc: 1, radius: innerRadius, sweep: 0 }), arcTo({ end: innerStart, largeArc: 1, radius: innerRadius, sweep: 0 }), "Z" ].join(" "); } return [ moveTo(outerStart), arcTo({ end: outerEnd, largeArc, radius, sweep: 1 }), lineTo(innerEnd), arcTo({ end: innerStart, largeArc, radius: innerRadius, sweep: 0 }), "Z" ].join(" "); }; export const buildPieArcs = ({ slices, centerX, centerY, radius, innerRadius = 0, startAngle = defaultStartAngle, endAngle = startAngle + fullCircle }) => { const safeRadius = isFinitePositive(radius) ? radius : 0; const safeInnerRadius = clamp(Number.isFinite(innerRadius) ? innerRadius : 0, 0, safeRadius); const safeStartAngle = Number.isFinite(startAngle) ? startAngle : defaultStartAngle; const safeEndAngle = Number.isFinite(endAngle) && endAngle > safeStartAngle ? endAngle : safeStartAngle + fullCircle; const total = slices.reduce((sum, slice) => slice.defined && isFinitePositive(slice.value ?? 0) ? sum + (slice.value ?? 0) : sum, 0); let cursor = safeStartAngle; return slices.map((slice) => { const value = slice.defined && isFinitePositive(slice.value ?? 0) ? (slice.value ?? 0) : null; const percentage = total > 0 && value !== null ? value / total : 0; const span = (safeEndAngle - safeStartAngle) * percentage; const arcStartAngle = cursor; const arcEndAngle = cursor + span; const centroidAngle = arcStartAngle + span / 2; const centroidRadius = (safeInnerRadius + safeRadius) / 2; const centroid = polarToCartesian({ angle: centroidAngle, centerX, centerY, radius: centroidRadius }); const path = safeRadius > 0 && span > 0 ? getArcPath({ centerX, centerY, endAngle: arcEndAngle, innerRadius: safeInnerRadius, radius: safeRadius, startAngle: arcStartAngle }) : ""; cursor = arcEndAngle; return { index: slice.index, label: slice.label, value, percentage, startAngle: arcStartAngle, endAngle: arcEndAngle, ...(slice.color !== undefined ? { color: slice.color } : {}), centroid, defined: path.length > 0, path, raw: slice.raw }; }); };