var NS_URI = 'http://www.w3.org/2000/svg', POINT_RADIUS = 14, frame = document.querySelector('.frame--poly-distribution'), n_edges_slider_el = document.getElementById('n-edges-slider'), n_edges_text_el = document.getElementById('n-edges-text'), m_layers_slider_el = document.getElementById('m-layers-slider'), m_layers_text_el = document.getElementById('m-layers-text'), min = Math.min, round = Math.round, PI = Math.PI, sin = Math.sin, cos = Math.cos, distribution = null; /* * returns a random number between the min and max values * if min is omitted, it defaults to 0 * if max is omitted, it defaults to 1 */ var rand = function(max, min) { var b = max || 1, a = min || 0; return a + (b - a)*Math.random(); }; var setAttrs = function(el, o) { for(var a in o) { el.setAttribute(a, o[a]); } } var Point = function(x, y, r) { this.x = (x === 0 || x)?~~x:round(rand()); this.y = (y === 0 || y)?~~y:round(rand()); this.el = document.createElementNS(NS_URI, 'circle'); setAttrs(this.el, { 'cx': this.x, 'cy': this.y, 'r': (r)?r:POINT_RADIUS }); }; var Segment = function(start, end) { this.start = start || new Point(); this.end = end || new Point(); this.el = document.createElementNS(NS_URI, 'line'); setAttrs(this.el, { 'x1': this.start.x, 'y1': this.start.y, 'x2': this.end.x, 'y2': this.end.y }); }; var Polygon = function(n, cradius) { var central_angle = 2*PI/n, angle, x, y, points = ''; this.el = document.createElementNS(NS_URI, 'polygon'); this.el.classList.add('layer__polygon'); for(var i = 0; i < n; i++) { angle = i*central_angle; x = round(cradius*cos(angle)); y = round(cradius*sin(angle)); points += x + ',' + y + ' '; } this.el.setAttribute('points', points.trim()) }; var EdgeGroup = function(layer, idx, edge) { var s = edge/layer, highlight_h = 3*POINT_RADIUS, highlight_w = round((layer - 1)*s + highlight_h), highlight_x = round(-(highlight_w - s)/2), highlight_y = round(-highlight_h/2), highlight = document.createElementNS(NS_URI, 'rect'), points_frag = document.createDocumentFragment(); this.el = document.createElementNS(NS_URI, 'g'); this.edge = new Segment(new Point((edge - highlight_h)/2, 0), new Point(-(edge - highlight_h)/2, 0)); this.points = []; highlight.classList.add('layer__highlight'); setAttrs(highlight, { 'x': highlight_x, 'y': highlight_y, 'width': highlight_w, 'height': highlight_h, 'rx': highlight_h/2 }); this.edge.el.classList.add('layer__edge') for(var i = layer; i > 0; i--) { this.points.push(new Point(i*s - edge/2, 0)); this.points[layer - i].el.classList.add('layer__point'); points_frag.appendChild(this.points[layer - i].el); if(i === layer) { this.points[layer - i].el.classList.add('layer__point--vertex'); } } this.el.appendChild(highlight); this.el.appendChild(this.edge.el); this.el.appendChild(points_frag); }; var Layer = function(n_edges, idx, base_cradius) { var cradius = idx*base_cradius, edge = 2*cradius*sin(PI/n_edges), group_frag = document.createDocumentFragment(), central_angle = 2*PI/n_edges, angle, iradius = round(cradius*cos(central_angle/2)); this.poly = new Polygon(n_edges, cradius); this.edge_groups = []; this.el = document.createElementNS(NS_URI, 'g'); this.el.classList.add('layer'); if(idx > 0) { for(var i = 0; i < n_edges; i++) { angle = (i + .5)*central_angle; this.edge_groups.push(new EdgeGroup(idx, i, edge)); group_frag.appendChild(this.edge_groups[i].el); setAttrs(this.edge_groups[i].el, { 'transform': 'rotate(' + round(angle*180/PI - 90) + ') ' + 'translate(0 ' + iradius + ')' }); } this.el.appendChild(this.poly.el); this.el.appendChild(group_frag); } }; var Distribution = function(n_edges, m_layers) { var vb = frame.getAttribute('viewBox').split(' '), w = ~~vb[2], h = ~~vb[3], base_cradius = round(.95*min(w, h)/2/(m_layers - 1)); this.n = n_edges; this.m = m_layers; this.layers = []; this.el = document.createElementNS(NS_URI, 'g'); for(var i = this.m - 1; i >= 0; i--) { this.layers.push(new Layer(n_edges, i, base_cradius)); this.el.appendChild(this.layers[this.m - i - 1].el); } this.el.classList.add('assembly'); this.el.setAttribute('transform', 'rotate(' + round(rand(360/n_edges)) + ')') frame.appendChild(this.el); }; var init = function() { if(distribution) { frame.removeChild(distribution.el); } distribution = new Distribution(n_edges_text_el.value, ~~m_layers_text_el.value + 1); }; init(); n_edges_slider_el.addEventListener('change', function() { if(this.value != distribution.n || this.value != n_edges_text_el.value) { n_edges_text_el.value = this.value; init(); } }, false); m_layers_slider_el.addEventListener('change', function() { if(this.value != distribution.m || this.value != m_layers_text_el.value) { m_layers_text_el.value = this.value; init(); } }, false); n_edges_text_el.addEventListener('blur', function() { if(this.value != distribution.n || this.value != n_edges_slider_el.value) { n_edges_slider_el.value = this.value; init(); } }, false); m_layers_text_el.addEventListener('blur', function() { if(this.value != distribution.m || this.value != m_layers_slider_el.value) { m_layers_slider_el.value = this.value; init(); } }, false); frame.addEventListener('click', function(e) { var t = e.target; if(t.classList.contains('layer__polygon')) { return; } if(t.classList.contains('layer__highlight')) { return; } }, false);