0b3eb009ff4c671c44bd1f5a0a92c742959e862c
[tech-radar.git] / src / graphing / radar.js
1 tr.graphing.Radar = function (size, radar) {
2 var self, fib, svg;
3
4 svg = d3.select("body").append("svg");
5 fib = new tr.util.Fib();
6
7 self = {};
8 self.svg = svg;
9
10 function center () {
11 return Math.round(size/2);
12 }
13
14 function plotLines() {
15 svg.append('line')
16 .attr('x1', center())
17 .attr('y1', 0)
18 .attr('x2', center())
19 .attr('y2', size)
20 .attr('stroke-width', 14);
21
22 svg.append('line')
23 .attr('x1', 0)
24 .attr('y1', center())
25 .attr('x2', size)
26 .attr('y2', center())
27 .attr('stroke-width', 14);
28 };
29
30 function getRadius(cycles, i) {
31 var sequence = fib.sequence(cycles.length);
32 var total = fib.sum(cycles.length);
33 var sum = fib.sum(i);
34
35 return center() - (center() * sum / total);
36 }
37
38 function plotCircles(cycles) {
39 var increment;
40
41 cycles.forEach(function (cycle, i) {
42 svg.append('circle')
43 .attr('cx', center())
44 .attr('cy', center())
45 .attr('r', getRadius(cycles, i));
46 });
47 }
48
49 function plotTexts(cycles) {
50 var increment;
51
52 increment = Math.round(center() / cycles.length);
53
54 cycles.forEach(function (cycle, i) {
55 svg.append('text')
56 .attr('class', 'line-text')
57 .attr('y', center() + 4)
58 .attr('x', center() - getRadius(cycles, i) + 10)
59 .text(cycle.name());
60
61 svg.append('text')
62 .attr('class', 'line-text')
63 .attr('y', center() + 4)
64 .attr('x', center() + getRadius(cycles, i) - 10)
65 .attr('text-anchor', 'end')
66 .text(cycle.name());
67 });
68 };
69
70 function triangle(x, y, cssClass) {
71 return svg.append('path')
72 .attr('d', 'M412.201,311.406c0.021,0,0.042,0,0.063,0c0.067,0,0.135,0,0.201,0c4.052,0,6.106-0.051,8.168-0.102c2.053-0.051,4.115-0.102,8.176-0.102h0.103c6.976-0.183,10.227-5.306,6.306-11.53c-3.988-6.121-4.97-5.407-8.598-11.224c-1.631-3.008-3.872-4.577-6.179-4.577c-2.276,0-4.613,1.528-6.48,4.699c-3.578,6.077-3.26,6.014-7.306,11.723C402.598,306.067,405.426,311.406,412.201,311.406')
73 .attr('transform', 'translate(' + ((-421 * .73) + x) + ',' + ((-299 * 0.73) + y) + ') scale(0.73) ')
74 .attr('class', cssClass)
75 .attr('stroke-width', 2);
76
77 var tsize = 7
78
79 var top = y - tsize + 2;
80 var left = (x - tsize);
81 var right = (x + tsize);
82 var bottom = (y + tsize);
83
84 var points = x + ',' + top + ' ' + left + ',' + bottom + ' ' + right + ',' + bottom;
85 }
86
87 function circle(x, y, cssClass) {
88 svg.append('circle')
89 .attr('cx', x)
90 .attr('cy', y)
91 .attr('class', cssClass)
92 .attr('stroke-width', 1.5)
93 .attr('r', 10);
94 }
95
96 function plotBlips(cycles, quadrant, adjustX, adjustY) {
97 var blips, number;
98 blips = quadrant.blips();
99 number = 0;
100 cycles.forEach(function (cycle, i) {
101 var maxRadius, minRadius, cycleBlips;
102
103 maxRadius = getRadius(cycles, i);
104 minRadius = (i == cycles.length - 1) ? 0: getRadius(cycles, i + 1);
105
106 var cycleBlips = blips.filter(function (blip) {
107 return blip.cycle() == cycle;
108 });
109
110 cycleBlips.forEach(function (blip) {
111 var angleInRad, radius;
112
113 var split = blip.name().split('');
114 var sum = split.reduce(function (p, c) { return p + c.charCodeAt(0); }, 0);
115 chance = new Chance(sum * cycle.name().length * number);
116
117 angleInRad = Math.PI * chance.integer({ min: 13, max: 85 }) / 180;
118 radius = chance.floating({ min: minRadius + 25, max: maxRadius - 10 });
119
120 var x = center() + radius * Math.cos(angleInRad) * adjustX;
121 var y = center() + radius * Math.sin(angleInRad) * adjustY;
122
123 if (blip.isNew()) {
124 triangle(x, y, cssClassFor(quadrant.name()));
125 } else {
126 circle(x, y, cssClassFor(quadrant.name()));
127 }
128
129 svg.append('text')
130 .attr('x', x)
131 .attr('y', y + 4)
132 .attr('class', 'blip-text')
133 .attr('text-anchor', 'middle')
134 .text(++number)
135 });
136 });
137 };
138
139 function cssClassFor(string) {
140 return string.toLowerCase().replace(/\s\&/g, '').replace(/\s/g, '-');
141 }
142
143 function plotQuadrantNames(quadrants) {
144 function plotName(name, anchor, x, y) {
145 svg.append('text')
146 .attr('x', x)
147 .attr('y', y)
148 .attr('class', cssClassFor(name))
149 .attr('text-anchor', anchor)
150 .text(name);
151 }
152
153 plotName(quadrants.I.name(), 'end', size - 10, 10)
154 plotName(quadrants.II.name(), 'start', 10, 10)
155 plotName(quadrants.III.name(), 'start', 10, size - 10)
156 plotName(quadrants.IV.name(), 'end', size -10, size - 10)
157 }
158
159 self.plot = function () {
160 var cycles, quadrants;
161
162 cycles = radar.cycles().reverse();
163 quadrants = radar.quadrants();
164
165 svg.attr('width', size).attr('height', size);
166
167 plotCircles(cycles);
168 plotLines();
169 plotTexts(cycles);
170
171 if (radar.hasQuadrants()) {
172 plotQuadrantNames(quadrants);
173 plotBlips(cycles, quadrants.I, 1, -1);
174 plotBlips(cycles, quadrants.II, -1, -1);
175 plotBlips(cycles, quadrants.III, -1, 1);
176 plotBlips(cycles, quadrants.IV, 1, 1);
177 }
178 };
179
180 return self;
181 };