react-gear-chart
Version:

92 lines (82 loc) • 2.56 kB
JavaScript
// @flow
import { inRange } from 'lodash-es'
/**
* Aliases
*/
export const PI = Math.PI
export const SQRT2 = Math.SQRT2
export const SQRT1_2 = Math.SQRT1_2
export const Sin = Math.sin
export const Cos = Math.cos
export const Asin = Math.asin
export const TAU = 2 * PI // http://tauday.com/tau-manifesto
export function Deg2Rad(deg: number): number {
return deg * PI / 180
}
export function Rad2Deg(rad: number): number {
return rad * 180 / PI
}
export function Polar2Cartesian(radius: number, angle: number,
{ degree = true, baseX = 0, baseY = 0 }: {degree: boolean, baseX: number, baseY: number} = {}
): [number, number] {
if (degree) {
angle = Deg2Rad(angle)
}
return [ baseX + Cos(angle) * radius, baseY + Sin(angle) * radius ]
}
export function NormalizeAngleRange(start, end) {
let startAngle = start % 360
let endAngle = end % 360
if (startAngle >= endAngle) {
endAngle += 360
}
return [ startAngle, endAngle ]
}
/**
* Given an annulus with start/end angle and outer/inner radius, calculate a viewport
* that can hold it, and a center point relative to the viewport upper-left corner
*
* Algo: get all vertices, calulate greatest difference from there cartesian coordinates,
*
* Use Polar Coordinate System with convention:
* https://en.wikipedia.org/wiki/Polar_coordinate_system#/media/File:Polar_graph_paper.svg
*
* @param {number} startAngle, degree
* @param {number} endAngle, degree
* @param {number} outerR
* @param {number} innerR
*/
export function AnnulusViewport(
startAngle: number,
endAngle: number,
outerR: number,
innerR: number,
padding: number = 0
): [number, number, number, number] {
if (outerR <= innerR) {
throw Error('Invalid radius, the outer one is smaller than the inner')
}
[ startAngle, endAngle ] = NormalizeAngleRange(startAngle, endAngle)
let p2c = Polar2Cartesian
let vertice = [
p2c(innerR, startAngle), p2c(innerR, endAngle),
p2c(outerR, startAngle), p2c(outerR, endAngle)
]
let _end = startAngle > endAngle ? endAngle + 360 : endAngle
// polars: East North West South
;[0, 90, 180, 270, 360].forEach(p => {
if (inRange(p, startAngle, _end)) {
vertice.push(p2c(outerR, p))
}
})
let minX, minY, maxX, maxY
minX = minY = Infinity
maxX = maxY = -Infinity
vertice.forEach(([x, y]) => {
if (x > maxX) maxX = x
if (x < minX) minX = x
if (y > maxY) maxY = y
if (y < minY) minY = y
})
return [maxX - minX + padding, maxY - minY + padding, -minX + padding / 2, maxY + padding / 2]
}