diff --git a/packages/vx-demo/components/gallery.js b/packages/vx-demo/components/gallery.js index 0ce36571c..69658489e 100644 --- a/packages/vx-demo/components/gallery.js +++ b/packages/vx-demo/components/gallery.js @@ -30,6 +30,7 @@ import Network from '../components/tiles/network'; import Streamgraph from '../components/tiles/streamgraph'; import Pack from '../components/tiles/pack'; import Treemap from '../components/tiles/treemap'; +import Radar from '../components/tiles/radar'; const items = [ '#242424', @@ -97,6 +98,7 @@ export default class Gallery extends React.Component { const t20 = this.state.dimensions[19] || [8, 300]; const t21 = this.state.dimensions[20] || [8, 300]; const t22 = this.state.dimensions[21] || [8, 300]; + const t23 = this.state.dimensions[22] || [8, 300]; return (
@@ -652,7 +654,34 @@ export default class Gallery extends React.Component {
- {false &&
} + + +
this.nodes.add(d)} + style={{ + background: '#FAF7E9', + }} + > +
+ +
+
+
Radar
+
+
{``}
+
+
+
+ +
+ {
} + {
}
diff --git a/packages/vx-demo/components/tiles/radar.js b/packages/vx-demo/components/tiles/radar.js new file mode 100644 index 000000000..5222d5083 --- /dev/null +++ b/packages/vx-demo/components/tiles/radar.js @@ -0,0 +1,124 @@ +import React from 'react'; +import { Group } from '@vx/group'; +import { letterFrequency } from '@vx/mock-data'; +import { scaleLinear } from '@vx/scale'; +import { Point } from '@vx/point'; +import { Line, LineRadial } from '@vx/shape'; +import { max, min } from 'd3-array'; + + +const ANG = 360; + +const _data = letterFrequency.slice(2, 12); +const calcAxis = (length) => { + if (!length) return []; + else return new Array(length + 1) + .fill(0).map((v, i) => ({ angle: i * (ANG / length)})); +} + +function calcPoints (length, radius) { + const step = Math.PI * 2 / length; + return new Array(length) + .fill(0).map((v, i) => { + return { + x: radius * Math.sin(i * step), + y: radius * Math.cos(i * step), + } + }); +} + +function calcCoordinates (data, scale, access) { + const step = Math.PI * 2 / data.length; + const points = new Array(data.length).fill({}); + const pointStr = new Array(data.length + 1) + .fill('').reduce((res, v, i) => { + if (i > data.length) return res; + const x = scale(access(data[i - 1])) * Math.sin(i * step); + const y = scale(access(data[i - 1])) * Math.cos(i * step); + points[i - 1] = { x, y }; + return res += `${x},${y} `; + }); + + points.str = pointStr; + return points; +} + +export default ({ + width, + height, + events = false, + margin = { top: 80, left: 80, right: 80, bottom: 80 }, + levels = 5, +}) => { + if (width < 10) return null; + + const webs = calcAxis(_data.length); + const radius = min([width, height]) / 2 - max(Object.values(margin)); + const points = calcPoints(_data.length, radius); + const labelMargin = max(Object.values(margin)) - 20; + + const x = d => d.letter; + const y = d => d.frequency; + + const rScale = scaleLinear({ + range: [0, Math.PI * 2], + domain: [ANG, 0], + }); + + const yScale = scaleLinear({ + range: [0, radius], + domain: [0, max(_data, y)], + }); + + const polyPoints = calcCoordinates(_data, yScale, y); + + return ( + + + {[...new Array(levels)].map((v, i) => ( + rScale(d.angle)} + radius={(i + 1) * radius / levels} + fill="none" + stroke="#d9d9d9" + strokeWidth={2} + strokeOpacity={0.8} + strokeLinecap="round" + /> + ))} + + + {[...new Array(_data.length)].map((v, i) => ( + + ))} + + + + + + {polyPoints.map((v, i) => ( + + ))} + + + ); +}; diff --git a/packages/vx-demo/pages/radar.js b/packages/vx-demo/pages/radar.js new file mode 100644 index 000000000..ba7edb15e --- /dev/null +++ b/packages/vx-demo/pages/radar.js @@ -0,0 +1,135 @@ +import React from 'react'; +import Show from '../components/show'; +import Radar from '../components/tiles/radar'; + +export default () => { + return ( + + {`import React from 'react'; +import { Group } from '@vx/group'; +import { letterFrequency } from '@vx/mock-data'; +import { scaleLinear } from '@vx/scale'; +import { Point } from '@vx/point'; +import { Line, LineRadial } from '@vx/shape'; +import { max, min } from 'd3-array'; + + +const ANG = 360; + +const _data = letterFrequency.slice(2, 12); +const calcAxis = (length) => { + if (!length) return []; + else return new Array(length + 1) + .fill(0).map((v, i) => ({ angle: i * (ANG / length)})); +} + +function calcPoints (length, radius) { + const step = Math.PI * 2 / length; + return new Array(length) + .fill(0).map((v, i) => { + return { + x: radius * Math.sin(i * step), + y: radius * Math.cos(i * step), + } + }); +} + +function calcCoordinates (data, scale, access) { + const step = Math.PI * 2 / data.length; + const points = new Array(data.length).fill({}); + const pointStr = new Array(data.length + 1) + .fill('').reduce((res, v, i) => { + if (i > data.length) return res; + const x = scale(access(data[i - 1])) * Math.sin(i * step); + const y = scale(access(data[i - 1])) * Math.cos(i * step); + points[i - 1] = { x, y }; + return res += \`$\{x\},$\{y\} \`; + }); + + points.str = pointStr; + return points; +} + +export default ({ + width, + height, + events = false, + margin = { top: 80, left: 80, right: 80, bottom: 80 }, + levels = 5, +}) => { + if (width < 10) return null; + + const webs = calcAxis(_data.length); + const radius = min([width, height]) / 2 - max(Object.values(margin)); + const points = calcPoints(_data.length, radius); + const labelMargin = max(Object.values(margin)) - 20; + + const x = d => d.letter; + const y = d => d.frequency; + + const rScale = scaleLinear({ + range: [0, Math.PI * 2], + domain: [ANG, 0], + }); + + const yScale = scaleLinear({ + range: [0, radius], + domain: [0, max(_data, y)], + }); + + const polyPoints = calcCoordinates(_data, yScale, y); + + return ( + + + {[...new Array(levels)].map((v, i) => ( + rScale(d.angle)} + radius={(i + 1) * radius / levels} + fill="none" + stroke="#d9d9d9" + strokeWidth={2} + strokeOpacity={0.8} + strokeLinecap="round" + /> + ))} + + + {[...new Array(_data.length)].map((v, i) => ( + + ))} + + + + + + {polyPoints.map((v, i) => ( + + ))} + + + ); +}; +`} + + ); +};