console.clear(); var $dev = { logo: function module() { let offset, R, grid; // warp unit square to unit circle // http://mathproofs.blogspot.com.es/2005/07/mapping-square-to-circle.html // (we use this to to mimic envelope warp effect) function warp(p) { return { x: p.x * Math.sqrt(1 - Math.pow(p.y/R, 2) / 2), y: p.y * Math.sqrt(1 - Math.pow(p.x/R, 2) / 2) }; }; // dot rendering function // renders one dot centered on {d.x, d.y} and warps it from unit square to unit circle function dot(d) { let render = ((x,y) => { let p1 = { x: d.x + x, y: d.y + y }; let p2 = warp(p1); return `${p2.x}, ${p2.y}` }) // approximation of a circle composed of 4 bezier curves // http://spencermortensen.com/articles/bezier-circle/ // (we draw these instead of plain elements so we can distort their geometry) let K = 0.551915024494 * r; // magic coefficient from Mortensen article return ` M ${render(-r, 0)} C ${render(-r, -K)} ${render(-K, -r)} ${render( 0, -r)} C ${render( K, -r)} ${render( r, -K)} ${render( r, 0)} C ${render( r, K)} ${render( K, r)} ${render( 0, r)} C ${render(-K, r)} ${render(-r, K)} ${render(-r, 0)} z ` } function bar(d, i) { if (!d) return; let render = ((x,y) => { let p1 = {x, y}; let p2 = warp(p1); return `${p2.x}, ${p2.y}` }) let midpoint = (p1, p2) => { return { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 } }; let p1 = { x: i * sp - offset, y: R*2 - sp - offset + r } let p2 = { x: i * sp - offset, y: R*2 - sp*d - offset + r } let m = midpoint(p1, p2); let c1 = midpoint(p1, m); let c2 = midpoint (m, p2); return ` M ${render(p1.x, p1.y)} C ${render(c1.x, c1.y )} ${render(c1.x, c1.y)} ${render(m.x, m.y)} C ${render(c2.x, c2.y )} ${render(c2.x, c2.y)} ${render(p2.x, p2.y)} ` } function exports(_selection) { _selection.each(function(_data) { R = sp * (N - 1) / 2 + r; // radius of the logo's bounding circle offset = sp * (N - 1) / 2; // H & V distance to shift everything from Q1 to center // Create array of coordinates of center points // (square NxN grid of points, with center of grid at 0,0) let range = [...Array(N).keys()]; // [0..N] let grid = range.map((y) => { return range.map((x) => { return { key: `${x}_${y}`, x: x * sp - offset, y: y * sp - offset } }) }); // apply pattern for (var col = 0; col < N; col++) { for (var row = 0; row < N; row++) { grid[N - row - 1][col].on = (row < pattern[col]); } } var svg = d3.select(this) .attr({ width: size, height: size, viewBox: `-${R} -${R} ${R*2} ${R*2}` // expand logo to fill SVG exactly }) var rows = svg.selectAll("g.row") .data(grid) rows.enter() .append("g") .classed("row", true); rows.exit().remove(); var dots = rows.selectAll("path.dot") .data((d) => d, (d) => d.key) dots.enter() .append("path") .classed("dot", true) dots .classed({ on: ((d) => d.on), off: ((d) => !d.on) }) .transition(1000) .attr("d", dot) dots.exit().remove() var bars = svg.selectAll("path.bar") .data(pattern) bars.enter() .append("path") .classed("bar", true) bars .style("stroke-width", r/3) .transition(1000) .attr("d", bar) bars.exit().remove() }); } // expose properties let size = 400; // size of svg in pixels exports.size = function(_) { if (!arguments.length) return size; size = _; return exports; } let pattern = [0,2,3,4,5,0] exports.pattern = function(_) { if (!arguments.length) return pattern; pattern = _; return exports; } let N = 6; // number of dots on each side exports.N = function(_) { if (!arguments.length) return N; N = _; return exports; } let sp = 3; // spacing from one dot to another exports.sp = function(_) { if (!arguments.length) return sp; sp = _; return exports; } let r = 1; // dot radius exports.r = function(_) { if (!arguments.length) return r; r = _; return exports; } return exports; } }; // 20191202