forked from derhuerst/svg-radar-chart
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
123 lines (101 loc) · 3.1 KB
/
index.js
File metadata and controls
123 lines (101 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
'use strict'
const h = require('virtual-dom/h')
// helpers
const round = v => Math.round(v * 10000) / 10000
const polarToX = (angle, distance) => Math.cos(angle - Math.PI / 2) * distance
const polarToY = (angle, distance) => Math.sin(angle - Math.PI / 2) * distance
const points = (points) => {
return points
.map(point => point[0].toFixed(4) + ',' + point[1].toFixed(4))
.join(' ')
}
const noSmoothing = (points) => {
let d = 'M' + points[0][0].toFixed(4) + ',' + points[0][1].toFixed(4)
for (let i = 1; i < points.length; i++) {
d += 'L' + points[i][0].toFixed(4) + ',' + points[i][1].toFixed(4)
}
return d + 'z'
}
const axis = (opt) => (col) => {
return h('polyline', Object.assign(opt.axisProps(col), {
points: points([
[0, 0], [
polarToX(col.angle, opt.chartSize / 2),
polarToY(col.angle, opt.chartSize / 2)
]
])
}))
}
const shape = (columns, opt) => (data, i) => {
return h('path', Object.assign(opt.shapeProps(data), {
d: opt.smoothing(columns.map((col) => {
const val = data[col.key]
if ('number' !== typeof val) {
throw new Error(`Data set ${i} is invalid.`)
}
return [
polarToX(col.angle, val * opt.chartSize / 2),
polarToY(col.angle, val * opt.chartSize / 2)
]
}))
}))
}
const scale = (opt, value) => {
return h('circle', Object.assign(opt.scaleProps(value), {
cx: 0, cy: 0, r: value * opt.chartSize / 2
}))
}
const caption = (opt) => (col) => {
return h('text', Object.assign(opt.captionProps(col), {
x: polarToX(col.angle, opt.size / 2 * .95).toFixed(4),
y: polarToY(col.angle, opt.size / 2 * .95).toFixed(4),
dy: (opt.captionProps(col).fontSize || 2) / 2
}), col.caption)
}
const defaults = {
size: 100, // size of the chart (including captions)
axes: true, // show axes?
scales: 3, // show scale circles?
captions: true, // show captions?
captionsPosition: 1.2, // where on the axes are the captions?
smoothing: noSmoothing, // shape smoothing function
axisProps: () => ({className: 'axis'}),
scaleProps: () => ({className: 'scale', fill: 'none'}),
shapeProps: () => ({className: 'shape'}),
captionProps: () => ({
className: 'caption',
textAnchor: 'middle', fontSize: 3,
fontFamily: 'sans-serif'
})
}
const render = (columns, data, opt = {}) => {
if ('object' !== typeof columns || Array.isArray(columns)) {
throw new Error('columns must be an object')
}
if (!Array.isArray(data)) {
throw new Error('data must be an array')
}
opt = Object.assign({}, defaults, opt)
opt.chartSize = opt.size / opt.captionsPosition
columns = Object.keys(columns).map((key, i, all) => ({
key, caption: columns[key],
angle: Math.PI * 2 * i / all.length
}))
const groups = [
h('g', data.map(shape(columns, opt)))
]
if (opt.captions) groups.push(h('g', columns.map(caption(opt))))
if (opt.axes) groups.unshift(h('g', columns.map(axis(opt))))
if (opt.scales > 0) {
const scales = []
for (let i = opt.scales; i > 0; i--) {
scales.push(scale(opt, i / opt.scales))
}
groups.unshift(h('g', scales))
}
const delta = (opt.size / 2).toFixed(4)
return h('g', {
transform: `translate(${delta},${delta})`
}, groups)
}
module.exports = render