UNPKG

recharts

Version:
184 lines (153 loc) 5.15 kB
import _ from 'lodash'; import { getPercentValue } from './DataUtils'; import { parseScale, checkDomainOfScale, getTicksOfScale } from './ChartUtils'; import { Coordinate, ChartOffset, GeometrySector } from './types'; export const RADIAN = Math.PI / 180; export const degreeToRadian = (angle: number) => (angle * Math.PI) / 180; export const radianToDegree = (angleInRadian: number) => (angleInRadian * 180) / Math.PI; export const polarToCartesian = (cx: number, cy: number, radius: number, angle: number): Coordinate => ({ x: cx + Math.cos(-RADIAN * angle) * radius, y: cy + Math.sin(-RADIAN * angle) * radius, }); export const getMaxRadius = ( width: number, height: number, offset: ChartOffset = { top: 0, right: 0, bottom: 0, left: 0, }, ) => Math.min( Math.abs(width - (offset.left || 0) - (offset.right || 0)), Math.abs(height - (offset.top || 0) - (offset.bottom || 0)), ) / 2; /** * Calculate the scale function, position, width, height of axes * @param {Object} props Latest props * @param {Object} axisMap The configuration of axes * @param {Object} offset The offset of main part in the svg element * @param {Object} axisType The type of axes, radius-axis or angle-axis * @param {String} chartName The name of chart * @return {Object} Configuration */ export const formatAxisMap = ( props: any, axisMap: any, offset: ChartOffset, axisType: 'angleAxis' | 'radiusAxis', chartName: string, ) => { const { width, height } = props; let { startAngle, endAngle } = props; const cx = getPercentValue(props.cx, width, width / 2); const cy = getPercentValue(props.cy, height, height / 2); const maxRadius = getMaxRadius(width, height, offset); const innerRadius = getPercentValue(props.innerRadius, maxRadius, 0); const outerRadius = getPercentValue(props.outerRadius, maxRadius, maxRadius * 0.8); const ids = Object.keys(axisMap); return ids.reduce((result, id) => { const axis = axisMap[id]; const { domain, reversed } = axis; let range; if (_.isNil(axis.range)) { if (axisType === 'angleAxis') { range = [startAngle, endAngle]; } else if (axisType === 'radiusAxis') { range = [innerRadius, outerRadius]; } if (reversed) { range = [range[1], range[0]]; } } else { ({ range } = axis); [startAngle, endAngle] = range; } const { realScaleType, scale } = parseScale(axis, chartName); scale.domain(domain).range(range); checkDomainOfScale(scale); const ticks = getTicksOfScale(scale, { ...axis, realScaleType }); const finalAxis = { ...axis, ...ticks, range, radius: outerRadius, realScaleType, scale, cx, cy, innerRadius, outerRadius, startAngle, endAngle, }; return { ...result, [id]: finalAxis }; }, {}); }; export const distanceBetweenPoints = (point: Coordinate, anotherPoint: Coordinate) => { const { x: x1, y: y1 } = point; const { x: x2, y: y2 } = anotherPoint; return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2); }; export const getAngleOfPoint = ({ x, y }: Coordinate, { cx, cy }: GeometrySector) => { const radius = distanceBetweenPoints({ x, y }, { x: cx, y: cy }); if (radius <= 0) { return { radius }; } const cos = (x - cx) / radius; let angleInRadian = Math.acos(cos); if (y > cy) { angleInRadian = 2 * Math.PI - angleInRadian; } return { radius, angle: radianToDegree(angleInRadian), angleInRadian }; }; export const formatAngleOfSector = ({ startAngle, endAngle }: GeometrySector) => { const startCnt = Math.floor(startAngle / 360); const endCnt = Math.floor(endAngle / 360); const min = Math.min(startCnt, endCnt); return { startAngle: startAngle - min * 360, endAngle: endAngle - min * 360, }; }; const reverseFormatAngleOfSetor = (angle: number, { startAngle, endAngle }: GeometrySector) => { const startCnt = Math.floor(startAngle / 360); const endCnt = Math.floor(endAngle / 360); const min = Math.min(startCnt, endCnt); return angle + min * 360; }; export const inRangeOfSector = ({ x, y }: Coordinate, sector: GeometrySector) => { const { radius, angle } = getAngleOfPoint({ x, y }, sector); const { innerRadius, outerRadius } = sector; if (radius < innerRadius || radius > outerRadius) { return false; } if (radius === 0) { return true; } const { startAngle, endAngle } = formatAngleOfSector(sector); let formatAngle = angle; let inRange; if (startAngle <= endAngle) { while (formatAngle > endAngle) { formatAngle -= 360; } while (formatAngle < startAngle) { formatAngle += 360; } inRange = formatAngle >= startAngle && formatAngle <= endAngle; } else { while (formatAngle > startAngle) { formatAngle -= 360; } while (formatAngle < endAngle) { formatAngle += 360; } inRange = formatAngle >= endAngle && formatAngle <= startAngle; } if (inRange) { return { ...sector, radius, angle: reverseFormatAngleOfSetor(formatAngle, sector) }; } return null; };