var color = d3.scaleQuantize() .domain([0, 1]) .range(["brown", "steelblue"]); color(0.49); // "brown" color(0.51); // "steelblue" //============================================================== // Instantiate new viz object //============================================================== let diamondViz = viz("diamondViz") diamondViz.width(800).height("100%"); diamondViz.viewBox(900,600); margin = {top: 30, bottom: 50, left: 65, right: 140} //============================================================== // Add mark sets //============================================================== let circles = diamondViz.newMarkSet("circles") .key(d=>d.id) .type("circle") //============================================================== // Define renderers //============================================================== let renderCircles = diamondViz.newRenderer("renderCircles") .markSet(circles) .enter( marks => { marks .style("transform-origin", d=>`${d.cx}px ${d.cy}px`) .attr("cx", d => d.cx) .attr("cy", d => d.cy) .attr("fill", d => d.fill) .attr("r", 6) .attr("fill-opacity", 0.7) .attr("cursor","pointer") .style("animation-delay", (d)=>`${500*Math.random()}ms`) .classed("animated bounceIn", true) }) .exit(marks => { marks .style("animation-delay", (d)=>`${200*Math.random()}ms`) .classed("animated fadeOut", true) .transition() .delay(500) .remove() }) let recolor = diamondViz.newRenderer("recolor") .markSet(circles) .update( marks => { marks .transition() .duration(500) .attr("fill", d => d.fill) }) //============================================================== // Create layers //============================================================== //check if d.color matches some value in filtered==false list let filterLayer = diamondViz.newLayer("filterLayer") .filter( function(d){ let keep = _.reject(diamondColors, 'filtered').map((d)=>d.color) return _.some(keep, (test)=> test===d.color) }) let main = diamondViz.newLayer("main") .scales({ xScale: function(){ scl = d3.scaleLinear() .domain(d3.extent(this._data, d=>d.x)) .range([0,1]) .nice() return scl }, yScale: function(){ scl = d3.scaleLinear() .domain(d3.extent(this._data, d=>d.y)) .range([1,0]) .nice() return scl }, fill: function(){ scl = d3.scaleOrdinal() .domain(_.uniq(this._data.map(d=>d.color))) .range(d3.schemeCategory10) return scl } }) .mappings({ xMap: function(val,ind,arr){ val.cx = this.scales().xScale()(val.x); return val }, yMap: function(val,ind,arr){ val.cy = this.scales().yScale()(val.y); return val }, fillMap: function(val,ind,arr){ val.fill = this.scales().fill()(val.color); return val } }) let scaleToViz = diamondViz.newLayer("scaleToViz") .scales({ x: function(){ scl = d3.scaleLinear() .domain([0,1]) .range([margin.left, diamondViz.viewBox()[0]-margin.right]) return scl }, y: function(){ scl = d3.scaleLinear() .domain([0,1]) .range([margin.top,diamondViz.viewBox()[1]-margin.bottom]) return scl } }) .mappings({ x: function(val,ind,arr){ val.cx = this.scales().x()(val.cx); return val }, y: function(val,ind,arr){ val.cy = this.scales().y()(val.cy); return val } }) //============================================================== // Connect layers & mark sets //============================================================== main._data = data; scaleToViz.onLayer(main); filterLayer.onLayer(scaleToViz); circles.onLayer(filterLayer); //============================================================== // Add interactions //============================================================== diamondViz.newInteraction("mouseenter-circle") .markSet(circles) .eventType("mouseenter") .listener(function(d, ind, nodes){ d3.event.stopPropagation(); d3.selectAll(nodes) .transition() .duration(1000) .attr("fill","grey") d3.select(this) .transition() .duration(500) .attr("fill","orange") }) diamondViz.newInteraction("mouseleave-circle") .markSet(circles) .eventType("mouseleave") .listener(function(d, ind, nodes){ d3.event.stopPropagation(); recolor.render() }) diamondViz.newInteraction("click-circle") .markSet(circles) .eventType("click") .listener(function(d, ind, nodes){ d3.event.stopPropagation(); let r = d3.select(this).attr("r"), isClicked = d3.select(this).classed("clicked"); //toggle "clicked" class on click; d3.select(this) .attr("pointer-events", "none") .classed("clicked", !isClicked) .transition("click") .duration(300) .attr("r", 5*r) d3.select(this) .transition("click") .delay(350) .attr("pointer-events", null) .duration(200) .attr("r", r) }) //============================================================== // Add frame //============================================================== let frameData = [{ id: 1, x: -0.02, y: 0, height: 1, width: 1 }] let frame = diamondViz.newMarkSet("frame") .key(d=>d.id) .type("rect") let renderFrame = diamondViz.newRenderer("renderFrame") .markSet(frame) .enter( marks => { marks .attr("x", d => d.x) .attr("y", d => d.y) .attr("width", d => d.width) .attr("height", d => d.height) .attr("stroke", "#888") .attr("fill","#EEE") .attr("fill-opacity",0.3) }) let frameLayer = diamondViz.newLayer("frameLayer") .scales({ x: scaleToViz.scales().x, y: scaleToViz.scales().y }) .mappings({ x: function(val,ind,arr){ val.x = this.scales().x()(val.x); return val }, y: function(val,ind,arr){ val.y = this.scales().y()(val.y); return val }, width: function(val,ind,arr){ val.width = this.scales().x()(val.width)-val.x; return val }, height: function(val,ind,arr){ val.height = this.scales().y()(val.height)-val.y; return val } }) diamondViz.newInteraction("click-frame") .markSet(frame) .eventType("click") .listener(function(d, ind, nodes){ d3.event.stopPropagation(); let point = d3.mouse(this) d3.select(this.parentNode) .insert("circle",".frame") .attr("cx", point[0]) .attr("cy", point[1]) .attr("fill", "#ddd") .attr("r",0) .attr("fill-opacity",.6) .transition("click") .ease(d3.easeCubicOut) .duration(750) .attr("r",350) .attr("fill-opacity",0) .remove() }) frameLayer._data = frameData; frame.onLayer(frameLayer); //============================================================== // Axes //============================================================== let xTicks = main.scales().xScale().ticks(10) .map(function(d, i){ return {id: i, xLabel: d, yPosition: frameData[0].height+0.01} }); let yTicks = main.scales().yScale().ticks(8) .map(function(d, i){ return {id: i, yLabel: d, xPosition: frameData[0].x-0.01} }); let xLabels = diamondViz.newMarkSet("xLabels") .key(d=>d.id) .type("text") let yLabels = diamondViz.newMarkSet("yLabels") .key(d=>d.id) .type("text") let hGrid = diamondViz.newMarkSet("hGrid") .key(d=>d.id) .type("line") let vGrid = diamondViz.newMarkSet("vGrid") .key(d=>d.id) .type("line") let xLabelRenderer = diamondViz.newRenderer("xLabelRenderer") .markSet(xLabels) .enter( marks => { marks .classed("labels",true) .attr("x", d => d.x) .attr("y", d => d.y) .style("text-anchor","middle") .style("dominant-baseline", "hanging") .text(d => d.xLabel) }) let yLabelRenderer = diamondViz.newRenderer("yLabelRenderer") .markSet(yLabels) .enter( marks => { marks .classed("labels",true) .attr("x", d => d.x) .attr("y", d => d.y) .style("text-anchor","end") .style("dominant-baseline", "central") .text(d => d.yLabel) }) let hGridRenderer = diamondViz.newRenderer("hGridRenderer") .markSet(hGrid) .enter( marks => { marks .attr("x1", d => d.x1) .attr("y1", d => d.y1) .attr("x2", d => d.x2) .attr("y2", d => d.y2) .attr("stroke", "#DDD") .attr("stroke-width", 1) }) let vGridRenderer = diamondViz.newRenderer("vGridRenderer") .markSet(vGrid) .enter( marks => { marks .attr("x1", d => d.x1) .attr("y1", d => d.y1) .attr("x2", d => d.x2) .attr("y2", d => d.y2) .attr("stroke", "#DDD") .attr("stroke-width", 1) }) let xLabelLayer = diamondViz.newLayer("xLabelLayer") .scales({ xmain: main.scales().xScale, x2viz: scaleToViz.scales().x, ymain: main.scales().yScale, y2viz: scaleToViz.scales().y }) .mappings({ x: function(val,ind,arr){ val.x = this.scales().x2viz()(this.scales().xmain()(val.xLabel)); return val }, y: function(val,ind,arr){ val.y = this.scales().y2viz()(val.yPosition); return val }, x1: function(val,ind,arr){ val.x1 = this.scales().x2viz()(this.scales().xmain()(val.xLabel)); return val }, x2: function(val,ind,arr){ val.x2 = this.scales().x2viz()(this.scales().xmain()(val.xLabel)); return val }, y1: function(val,ind,arr){ val.y1 = this.scales().y2viz()(val.yPosition); return val }, y2: function(val,ind,arr){ val.y2 = this.scales().y2viz()(frameData[0].y); return val } }) let yLabelLayer = diamondViz.newLayer("yLabelLayer") .scales({ xmain: main.scales().xScale, x2viz: scaleToViz.scales().x, ymain: main.scales().yScale, y2viz: scaleToViz.scales().y }) .mappings({ x: function(val,ind,arr){ val.x = this.scales().x2viz()(val.xPosition); return val }, y: function(val,ind,arr){ val.y = this.scales().y2viz()(this.scales().ymain()(val.yLabel)); return val }, x1: function(val,ind,arr){ val.x1 = this.scales().x2viz()(val.xPosition); return val }, x2: function(val,ind,arr){ val.x2 = this.scales().x2viz()(frameData[0].width); return val }, y1: function(val,ind,arr){ val.y1 = this.scales().y2viz()(this.scales().ymain()(val.yLabel)); return val }, y2: function(val,ind,arr){ val.y2 = this.scales().y2viz()(this.scales().ymain()(val.yLabel)); return val } }) xLabelLayer._data = xTicks; yLabelLayer._data = yTicks; xLabels.onLayer(xLabelLayer); yLabels.onLayer(yLabelLayer); vGrid.onLayer(xLabelLayer); hGrid.onLayer(yLabelLayer); //============================================================== // Color Legend //============================================================== let diamondColors = _.uniq(main._data.map(d=>d.color)) .map(function(d, i){ return { id: i, color: d, filtered: false, x: frameData[0].width+0.02, y: frameData[0].y + 0.06, size: 0.04, padding: 0.03, dx: 0.055 } }); let legendKey = diamondViz.newMarkSet("legendKey") .key(d=>d.id) .type("rect") let legendLabel = diamondViz.newMarkSet("legendLabel") .key(d=>d.id) .type("text") let legendKeyRenderer = diamondViz.newRenderer("legendKeyRenderer") .markSet(legendKey) .enter( marks => { marks .attr("x",d=>d.x) .attr("y",d=>d.y) .attr("height",d=>d.size) .attr("width",d=>d.size) .attr("rx",d=>d.size/5) .attr("ry",d=>d.size/5) .attr("fill",d=>d.fill) .attr("stroke",d=>d.stroke) .attr("stroke-width", 3) }) .update(marks => { marks .attr("fill",d=>d.fill) }) let legendLabelRenderer = diamondViz.newRenderer("legendLabelRenderer") .markSet(legendLabel) .enter( marks => { marks .classed("labels",true) .attr("x", d => d.x + d.dx) .attr("y", d => d.y + d.dy) .style("text-anchor","start") .style("dominant-baseline", "central") .text(d => d.color) }) let legendLayer = diamondViz.newLayer("legendLayer") .scales({ fill: main.scales().fill, x: scaleToViz.scales().x, y: scaleToViz.scales().y, }) .mappings({ x: function(val,ind,arr){ val.x = this.scales().x()(val.x); return val }, y: function(val,ind,arr){ val.y = this.scales().y()(val.y+(val.size+val.padding)*ind); return val }, stroke: function(val,ind,arr){ val.stroke = this.scales().fill()(val.color); return val }, fill: function(val,ind,arr){ val.fill = val.filtered ? "white" : this.scales().fill()(val.color); return val }, size: function(val,ind,arr){ val.size = this.scales().x()(val.size)-margin.left; return val }, dx: function(val,ind,arr){ val.dx = this.scales().x()(val.dx)-margin.left; return val }, dy: function(val,ind,arr){ val.dy = val.size/2; return val }, }) diamondViz.newInteraction("click-legendKey") .markSet(legendKey) .eventType("click") .listener(function(d, ind, nodes){ d3.event.stopPropagation(); d3.select(this) .each(function(d){ diamondColors[d.id].filtered = !diamondColors[d.id].filtered; }) legendKey.updateMarkData() legendKeyRenderer.render() circles.updateMarkData() renderCircles.render() }) legendLayer._data = diamondColors; legendKey.onLayer(legendLayer); legendLabel.onLayer(legendLayer); //============================================================== // Initialize visualization //============================================================== circles.updateMarkData() frame.updateMarkData() xLabels.updateMarkData() yLabels.updateMarkData() vGrid.updateMarkData() hGrid.updateMarkData() legendKey.updateMarkData() legendLabel.updateMarkData() diamondViz.init('#vizhost', [ renderFrame, xLabelRenderer, yLabelRenderer, vGridRenderer, hGridRenderer, legendKeyRenderer, legendLabelRenderer, renderCircles ]) //