/* script-mezzo.js transition a bunch of points from their current location to somewhere else. change color during transition. image -> halftone using simplistic algorithm a la https://en.wikipedia.org/wiki/Halftone ds: 2017-04-22 - 2017-04-25 */ // geometry of inner svg plotting area var margin = { top: 20, right: 20, bottom: 20, left: 20 }; // in line with aspect ratio of image (based on image) var width = 1.5 * 241 - margin.left - margin.right; var height = 1.5 * 299 - margin.top - margin.bottom; // details about the data var dataFileName = "zlogo-cart+pol.csv" var varNames = { "xData": "y", // swap! "yData": "x", "xData1": "y1", // swap! "yData1": "x1", "szData": "sz", }; // details about the transition var isAnimating = true; var transitionTime = 500; // ms var delayTime = 200; // ms var spiralJitter = 0.02; // proportion var iTransition = 0; // transition counter // setup x mapping var xValue = function(d) { return d[varNames.xData]; }; // data -> value var xScale = d3.scaleLinear().range([0, width]), // value -> display xMap = function(d) { return xScale(xValue(d)); }; // data -> display // setup y mapping var yValue = function(d) { return d[varNames.yData]; }; // data -> value var yScale = d3.scaleLinear().range([0, height]), // value -> display yMap = function(d) { return yScale(yValue(d)); }; // data -> display // dot matrix stuff var scatterRange = [10, 0.2]; // halftone points size var scatterScale = d3.scaleSqrt().domain([0, 1]).range(scatterRange); var scatterMap = function(d) { return scatterScale(d[varNames.szData]); }; // create SVG var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); function setupData() { d3.csv(dataFileName, type, function(error, data) { console.log("loading data from csv") console.log(d3.extent(data, function(d) { return d.x; })); // global vars xExtent = d3.extent(data, function(d) { return d[varNames.xData]; }); yExtent = d3.extent(data, function(d) { return d[varNames.yData]; }); szExtent = d3.extent(data, function(d) { return d[varNames.szData]; }); // keep a copy of the data around. picData = data; // set domain xScale.domain(xExtent); yScale.domain(yExtent); // create dot pattern svg.append("g").selectAll(".circle") .data(data) .enter().append("circle") .attr("transform", "translate(" + margin.left + ", 0)") .attr("cx", xMap) .attr("cy", yMap) .attr("r", scatterMap); /* hook for making this interactive d3.select("body") .append("svg").append("g") .on("mouseover", function() {console.log('in');} ) .on("mouseout", function() {console.log('out');} ); */ }); } // type conversion - helper function type(d) { // numeric data d[varNames.xData] = +d[varNames.xData]; d[varNames.yData] = +d[varNames.yData]; d[varNames.xData1] = +d[varNames.xData1] * 0.8; d[varNames.yData1] = +d[varNames.yData1] * 0.8; d[varNames.szData] = +d[varNames.szData]; return d; } function getData( update ) { var numValues = d3.selectAll('circle').data().length; if (+update == 0) { for (var i = 0; i < numValues; i++) { // Random # pairs var newNumber1 = Math.random() * xExtent[1]; var newNumber2 = Math.random() * yExtent[1]; dataset.push([newNumber1, newNumber2]); } } else if (+update == 1) { dataset = picData.map(function(i) { return [i.y, i.x]; }); } else { dataset = picData.map(function(i) { return [i.y1 + spiralJitter * xExtent[1] * Math.random(), i.x1 + spiralJitter * yExtent[1] * Math.random()]; }); } return dataset; } // On click, update with new data var animateData = function(update) { console.log("update " + update); iTransition += 1; // update counter dataset = []; // Initialize empty array dataset = getData( update ) ; // Update circles svg.selectAll("circle") .data(dataset) // Update with new data .transition() .delay(delayTime) .duration(transitionTime) .attr("fill", function() {return iTransition % 2 ? "red" : "black";} ) // Change color // .attr("r", scatterSize * 5) // Change size .ease(d3.easeCubic) .attr("cx", function(d) { return xScale(d[0]); // Circle's X }) .attr("cy", function(d) { return yScale(d[1]); // Circle's Y }) .on("end", function(d, i) { // only execute this once. the trick is to check for one, e.g. the first (sentinel) element in the array! if (i === 1 & isAnimating === true) { animateData(iTransition % 3); } }) } // call the setup. setupData();