Skip to content

Commit

Permalink
feat(pie): compute radial lables in arcs package
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Dec 18, 2020
1 parent 46af372 commit 1562576
Show file tree
Hide file tree
Showing 18 changed files with 379 additions and 374 deletions.
67 changes: 67 additions & 0 deletions packages/arcs/src/boundingBox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { positionFromAngle, degreesToRadians } from '@nivo/core'

/**
* Computes the bounding box for a circle arc.
*
* Assumptions:
* - Anywhere the arc intersects an axis will be a max or a min.
* - If the arc doesn't intersect an axis, then the center
* will be one corner of the bounding rectangle,
* and this is the only case when it will be.
* - The only other possible extreme points of the sector to consider
* are the endpoints of the radii.
*
* This script was built within the help of this answer on stackoverflow:
* https://stackoverflow.com/questions/1336663/2d-bounding-box-of-a-sector
*/
export const computeArcBoundingBox = (
centerX: number,
centerY: number,
radius: number,
// in degrees
startAngle: number,
// in degrees
endAngle: number,
includeCenter = true
) => {
let points: [number, number][] = []

const p0 = positionFromAngle(degreesToRadians(startAngle), radius)
points.push([p0.x, p0.y])

const p1 = positionFromAngle(degreesToRadians(endAngle), radius)
points.push([p1.x, p1.y])

for (
let angle = Math.round(Math.min(startAngle, endAngle));
angle <= Math.round(Math.max(startAngle, endAngle));
angle++
) {
if (angle % 90 === 0) {
const p = positionFromAngle(degreesToRadians(angle), radius)
points.push([p.x, p.y])
}
}

points = points.map(([x, y]) => [centerX + x, centerY + y])
if (includeCenter === true) {
points.push([centerX, centerY])
}

const xs = points.map(([x]) => x)
const ys = points.map(([, y]) => y)

const x0 = Math.min(...xs)
const x1 = Math.max(...xs)

const y0 = Math.min(...ys)
const y1 = Math.max(...ys)

return {
points,
x: x0,
y: y0,
width: x1 - x0,
height: y1 - y0,
}
}
26 changes: 5 additions & 21 deletions packages/arcs/src/centers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@ import { useTransition, to, SpringValue } from 'react-spring'
import {
// @ts-ignore
midAngle,
// @ts-ignore
positionFromAngle,
// @ts-ignore
radiansToDegrees,
useMotionConfig,
} from '@nivo/core'
import { Arc, DatumWithArc } from './types'
import { Arc, DatumWithArc, Point } from './types'
import { filterDataBySkipAngle } from './utils'
import { ArcTransitionMode, TransitionExtra, useArcTransitionMode } from './arcTransitionMode'

export const computeArcCenter = (
arc: Arc,
offset: number
): {
x: number
y: number
} => {
export const computeArcCenter = (arc: Arc, offset: number): Point => {
const angle = midAngle(arc) - Math.PI / 2
const radius = arc.innerRadius + (arc.outerRadius - arc.innerRadius) * offset

Expand Down Expand Up @@ -83,9 +75,7 @@ export const useArcCentersTransition = <Datum extends DatumWithArc, ExtraProps =
}
}

export interface ArcCenter<Datum extends DatumWithArc> {
x: number
y: number
export interface ArcCenter<Datum extends DatumWithArc> extends Point {
data: Datum
}

Expand Down Expand Up @@ -122,13 +112,7 @@ export const useArcCenters = <
}): (ArcCenter<Datum> & ExtraProps)[] =>
useMemo(
() =>
data
// filter out arcs with a length below `skipAngle`
.filter(
datum =>
Math.abs(radiansToDegrees(datum.arc.endAngle - datum.arc.startAngle)) >=
skipAngle
)
filterDataBySkipAngle<Datum>(data, skipAngle)
// compute position and extra props for each eligible datum
.map(datum => {
const position = computeArcCenter(datum.arc, offset)
Expand Down
2 changes: 2 additions & 0 deletions packages/arcs/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './arcTransitionMode'
export * from './boundingBox'
export * from './canvas'
export * from './centers'
export * from './interactivity'
Expand All @@ -8,4 +9,5 @@ export * from './types'
export * from './useAnimatedArc'
export * from './useArcGenerator'
export * from './useArcLabels'
export * from './useArcLinkLabelsTransition'
export * from './useArcsTransition'
49 changes: 22 additions & 27 deletions packages/arcs/src/links.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import { useCallback, useMemo } from 'react'
import {
// @ts-ignore
positionFromAngle,
// @ts-ignore
radiansToDegrees,
// @ts-ignore
getLabelGenerator,
useTheme,
} from '@nivo/core'
import { InheritedColorConfig, useInheritedColor } from '@nivo/colors'
import { DatumWithArc, DatumWithArcAndColor } from './types'
import { Arc, DatumWithArc, DatumWithArcAndColor, Point } from './types'
import { getNormalizedAngle } from './utils'

interface Point {
x: number
y: number
}

export interface ArcLink<Datum extends DatumWithArc> {
export interface ArcLink {
side: 'before' | 'after'
points: [Point, Point, Point]
}

export interface ArcLinkWithDatum<Datum extends DatumWithArc> extends ArcLink {
data: Datum
}

Expand All @@ -28,22 +24,19 @@ export interface ArcLink<Datum extends DatumWithArc> {
* please not that points coordinates are relative to
* the center of the arc.
*/
export const computeArcLink = <Datum extends DatumWithArc>(
datum: Datum,
export const computeArcLink = (
arc: Arc,
offset: number,
diagonalLength: number,
straightLength: number
): ArcLink<Datum> => {
): ArcLink => {
const centerAngle = getNormalizedAngle(
datum.arc.startAngle + (datum.arc.endAngle - datum.arc.startAngle) / 2 - Math.PI / 2
)
const point0: Point = positionFromAngle(centerAngle, datum.arc.outerRadius + offset)
const point1: Point = positionFromAngle(
centerAngle,
datum.arc.outerRadius + offset + diagonalLength
arc.startAngle + (arc.endAngle - arc.startAngle) / 2 - Math.PI / 2
)
const point0: Point = positionFromAngle(centerAngle, arc.outerRadius + offset)
const point1: Point = positionFromAngle(centerAngle, arc.outerRadius + offset + diagonalLength)

let side: ArcLink<Datum>['side']
let side: ArcLink['side']
let point2: Point
if (centerAngle < Math.PI / 2 || centerAngle > Math.PI * 1.5) {
side = 'after'
Expand All @@ -62,7 +55,6 @@ export const computeArcLink = <Datum extends DatumWithArc>(
return {
side,
points: [point0, point1, point2],
data: datum,
}
}

Expand Down Expand Up @@ -95,9 +87,9 @@ export const useArcLinks = <
straightLength: number
// this can be used to append extra properties to the links,
// can be used to compute a color/label for example.
computeExtraProps?: (datum: ArcLink<Datum>) => ExtraProps
}): (ArcLink<Datum> & ExtraProps)[] => {
const links: ArcLink<Datum>[] = useMemo(
computeExtraProps?: (datum: ArcLinkWithDatum<Datum>) => ExtraProps
}): (ArcLinkWithDatum<Datum> & ExtraProps)[] => {
const links = useMemo(
() =>
data
// filter out arcs with a length below `skipAngle`
Expand All @@ -107,7 +99,10 @@ export const useArcLinks = <
skipAngle
)
// compute the link for each eligible arc
.map(datum => computeArcLink<Datum>(datum, offset, diagonalLength, straightLength)),
.map(datum => ({
...computeArcLink(datum.arc, offset, diagonalLength, straightLength),
data: datum,
})),
[data, skipAngle, offset, diagonalLength, straightLength]
)

Expand All @@ -123,7 +118,7 @@ export const useArcLinks = <
)
}

export interface ArcLinkLabel<Datum extends DatumWithArcAndColor> extends ArcLink<Datum> {
export interface ArcLinkLabel<Datum extends DatumWithArcAndColor> extends ArcLinkWithDatum<Datum> {
x: number
y: number
label: string
Expand Down Expand Up @@ -168,7 +163,7 @@ export const useArcLinkLabels = <Datum extends DatumWithArcAndColor>({
const getTextColor = useInheritedColor<Datum>(textColor, theme)

const computeExtraProps = useCallback(
(link: ArcLink<Datum>) => {
(link: ArcLinkWithDatum<Datum>) => {
const position = {
x: link.points[2].x,
y: link.points[2].y,
Expand All @@ -193,7 +188,7 @@ export const useArcLinkLabels = <Datum extends DatumWithArcAndColor>({
[getLabel, getLinkColor, getTextColor]
)

return useArcLinks<Datum, Omit<ArcLinkLabel<Datum>, keyof ArcLink<Datum>>>({
return useArcLinks<Datum, Omit<ArcLinkLabel<Datum>, keyof ArcLinkWithDatum<Datum>>>({
data,
skipAngle,
offset,
Expand Down
5 changes: 5 additions & 0 deletions packages/arcs/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Arc as D3Arc } from 'd3-shape'

export interface Point {
x: number
y: number
}

export interface Arc {
// start angle in radians
startAngle: number
Expand Down
2 changes: 1 addition & 1 deletion packages/arcs/src/useArcGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ArcGenerator, Arc } from './types'

/**
* Memoize a d3 arc generator.
*
*
* Please note that both inner/outer radius should come
* aren't static and should come from the arc itself,
* while it requires more props on the arcs, it provides
Expand Down
Loading

0 comments on commit 1562576

Please sign in to comment.