X-Git-Url: https://pwan.org/git/?p=tech-radar.git;a=blobdiff_plain;f=examples%2Ftech-radar.js;fp=examples%2Ftech-radar.js;h=8f26cb2f42160c20d1f040f80e8efec46607ffd2;hp=0000000000000000000000000000000000000000;hb=00e75b3dd3d8b1c07046ef8685a9aff976aa41e0;hpb=3cd044c7edd4b73fac124a43fcdccb3a00f5811e diff --git a/examples/tech-radar.js b/examples/tech-radar.js new file mode 100644 index 0000000..8f26cb2 --- /dev/null +++ b/examples/tech-radar.js @@ -0,0 +1,440 @@ +/** + * tech-radar + * @version v0.1.6 + */ +var tr = tr || {}; +tr.models = {}; +tr.graphing = {}; +tr.util = {}; + +tr.graphing.Radar = function (size, radar) { + var self, fib, svg; + + fib = new tr.util.Fib(); + + self = {}; + self.svg = function () { + return svg; + } + + function center () { + return Math.round(size/2); + } + + function plotLines() { + svg.append('line') + .attr('x1', center()) + .attr('y1', 0) + .attr('x2', center()) + .attr('y2', size) + .attr('stroke-width', 14); + + svg.append('line') + .attr('x1', 0) + .attr('y1', center()) + .attr('x2', size) + .attr('y2', center()) + .attr('stroke-width', 14); + }; + + function getRadius(cycles, i) { + var sequence = fib.sequence(cycles.length); + var total = fib.sum(cycles.length); + var sum = fib.sum(i); + + return center() - (center() * sum / total); + } + + function plotCircles(cycles) { + var increment; + + cycles.forEach(function (cycle, i) { + svg.append('circle') + .attr('cx', center()) + .attr('cy', center()) + .attr('r', getRadius(cycles, i)); + }); + } + + function plotTexts(cycles) { + var increment; + + increment = Math.round(center() / cycles.length); + + cycles.forEach(function (cycle, i) { + svg.append('text') + .attr('class', 'line-text') + .attr('y', center() + 4) + .attr('x', center() - getRadius(cycles, i) + 10) + .text(cycle.name()); + + svg.append('text') + .attr('class', 'line-text') + .attr('y', center() + 4) + .attr('x', center() + getRadius(cycles, i) - 10) + .attr('text-anchor', 'end') + .text(cycle.name()); + }); + }; + + function triangle(x, y, cssClass) { + var tsize, top, left, right, bottom, points; + + tsize = 13 + top = y - tsize; + left = (x - tsize + 1); + right = (x + tsize + 1); + bottom = (y + tsize - tsize / 2.5); + + points = x + 1 + ',' + top + ' ' + left + ',' + bottom + ' ' + right + ',' + bottom; + + return svg.append('polygon') + .attr('points', points) + .attr('class', cssClass) + .attr('stroke-width', 1.5); + } + + function circle(x, y, cssClass) { + svg.append('circle') + .attr('cx', x) + .attr('cy', y) + .attr('class', cssClass) + .attr('stroke-width', 1.5) + .attr('r', 10); + } + + function plotBlips(cycles, quadrant, adjustX, adjustY, cssClass) { + var blips; + blips = quadrant.blips(); + cycles.forEach(function (cycle, i) { + var maxRadius, minRadius, cycleBlips; + + maxRadius = getRadius(cycles, i); + minRadius = (i == cycles.length - 1) ? 0: getRadius(cycles, i + 1); + + var cycleBlips = blips.filter(function (blip) { + return blip.cycle() == cycle; + }); + + cycleBlips.forEach(function (blip) { + var angleInRad, radius; + + var split = blip.name().split(''); + var sum = split.reduce(function (p, c) { return p + c.charCodeAt(0); }, 0); + chance = new Chance(sum * cycle.name().length * blip.number()); + + angleInRad = Math.PI * chance.integer({ min: 13, max: 85 }) / 180; + radius = chance.floating({ min: minRadius + 25, max: maxRadius - 10 }); + + var x = center() + radius * Math.cos(angleInRad) * adjustX; + var y = center() + radius * Math.sin(angleInRad) * adjustY; + + if (blip.isNew()) { + triangle(x, y, cssClass); + } else { + circle(x, y, cssClass); + } + + svg.append('text') + .attr('x', x) + .attr('y', y + 4) + .attr('class', 'blip-text') + .attr('text-anchor', 'middle') + .text(blip.number()) + }); + }); + }; + + function plotQuadrantNames(quadrants) { + function plotName(name, anchor, x, y, cssClass) { + svg.append('text') + .attr('x', x) + .attr('y', y) + .attr('class', cssClass) + .attr('text-anchor', anchor) + .text(name); + } + + plotName(quadrants.I.name(), 'end', size - 10, 10, 'first') + plotName(quadrants.II.name(), 'start', 10, 10, 'second') + plotName(quadrants.III.name(), 'start', 10, size - 10, 'third') + plotName(quadrants.IV.name(), 'end', size -10, size - 10, 'fourth') + } + + self.init = function (selector) { + svg = d3.select(selector || 'body').append("svg"); + return self; + }; + + self.plot = function () { + var cycles, quadrants; + + cycles = radar.cycles().reverse(); + quadrants = radar.quadrants(); + + svg.attr('width', size).attr('height', size); + + plotCircles(cycles); + plotLines(); + plotTexts(cycles); + + if (radar.hasQuadrants()) { + plotQuadrantNames(quadrants); + plotBlips(cycles, quadrants.I, 1, -1, 'first'); + plotBlips(cycles, quadrants.II, -1, -1, 'second'); + plotBlips(cycles, quadrants.III, -1, 1, 'third'); + plotBlips(cycles, quadrants.IV, 1, 1, 'fourth'); + } + }; + + return self; +}; + +tr.graphing.RefTable = function (radar) { + var self = {}; + var injectionElement; + + function blipsByCycle () { + // set up empty blip arrays for each cycle + var cycles = {}; + radar.cycles() + .map(function (cycle) { + return { + order: cycle.order(), + name: cycle.name() + }; + }) + .sort(function (a, b) { + if (a.order === b.order) { + return 0; + } else if (a.order < b.order) { + return -1; + } else { + return 1; + } + }) + .forEach(function (cycle) { + cycles[cycle.name] = []; + }); + + // group blips by cycle + var blips = []; + var quadrants = radar.quadrants(); + Object.keys(quadrants).forEach(function (quadrant) { + blips = blips.concat(quadrants[quadrant].blips()); + }); + + blips.forEach(function (blip) { + cycles[blip.cycle().name()].push(blip); + }); + + return cycles; + } + + self.init = function (selector) { + injectionElement = document.querySelector(selector || 'body'); + return self; + }; + + self.render = function () { + var blips = blipsByCycle(); + + var html = ''; + + Object.keys(blips).forEach(function (cycle) { + html += ''; + + blips[cycle].forEach(function (blip) { + html += '' + + '' + + '' + + '' + + ''; + }); + }); + + html += '
' + cycle + '
' + blip.number() + '' + blip.name() + '' + blip.description() + '
'; + + injectionElement.innerHTML = html; + }; + + return self; +}; + +tr.models.Blip = function (name, cycle, isNew, description) { + var self, number; + + self = {}; + number = -1; + + self.name = function () { + return name; + }; + + self.description = function () { + return description || ''; + }; + + self.isNew = function () { + return isNew; + }; + + self.cycle = function () { + return cycle; + }; + + self.number = function () { + return number; + }; + + self.setNumber = function (newNumber) { + number = newNumber; + }; + + return self; +}; + +tr.models.Cycle = function (name, order) { + var self = {}; + + self.name = function () { + return name; + }; + + self.order = function () { + return order; + }; + + return self; +}; + +tr.models.Quadrant = function (name) { + var self, blips; + + self = {}; + blips = []; + + self.name = function () { + return name; + }; + + self.add = function (newBlips) { + if (Array.isArray(newBlips)) { + blips = blips.concat(newBlips); + } else { + blips.push(newBlips); + } + }; + + self.blips = function () { + return blips.slice(0); + }; + + return self; +}; + +tr.models.Radar = function() { + var self, quadrants, blipNumber; + + blipNumber = 0; + quadrants = { I: null, II: null, III: null, IV: null }; + self = {}; + + function setNumbers(blips) { + blips.forEach(function (blip) { + blip.setNumber(++blipNumber); + }); + } + + self.setFirstQuadrant = function (quadrant) { + quadrants.I = quadrant; + setNumbers(quadrants.I.blips()); + }; + + self.setSecondQuadrant = function (quadrant) { + quadrants.II = quadrant; + setNumbers(quadrants.II.blips()); + }; + + self.setThirdQuadrant = function (quadrant) { + quadrants.III = quadrant; + setNumbers(quadrants.III.blips()); + }; + + self.setFourthQuadrant = function (quadrant) { + quadrants.IV = quadrant; + setNumbers(quadrants.IV.blips()); + }; + + function allQuadrants() { + var all = []; + + for (var p in quadrants) { + if (quadrants.hasOwnProperty(p) && quadrants[p] != null) { + all.push(quadrants[p]); + } + } + + return all; + } + + function allBlips() { + return allQuadrants().reduce(function (blips, quadrant) { + return blips.concat(quadrant.blips()); + }, []); + } + + self.hasQuadrants = function () { + return !!quadrants.I || !!quadrants.II || !!quadrants.III || !!quadrants.IV; + } + + self.cycles = function () { + var cycleHash, cycleArray; + + cycleArray = []; + cycleHash = {}; + + allBlips().forEach(function (blip) { + cycleHash[blip.cycle().name()] = blip.cycle(); + }); + + for (var p in cycleHash) { + if (cycleHash.hasOwnProperty(p)) { + cycleArray.push(cycleHash[p]); + } + } + + return cycleArray.slice(0).sort(function (a, b) { return a.order() - b.order(); }); + }; + + self.quadrants = function () { + return quadrants; + }; + + return self; +}; + +tr.util.Fib = function () { + var self = {}; + + self.sequence = function (length) { + var result = [0, 1]; + + for (var i = 2; i < length; i++) { + result[i] = result[i-2] + result[i-1]; + } + + return result; + }; + + self.sum = function (length) { + if (length === 0) { return 0; } + if (length === 1) { return 1; } + + return self.sequence(length + 1).reduce(function (previous, current) { + return previous + current; + }, 0); + }; + + return self; +};