Skip to content

Instantly share code, notes, and snippets.

@ckothari
Last active August 30, 2016 16:50
Show Gist options
  • Save ckothari/848b803d6650b1eacfd1d2d7de12cc14 to your computer and use it in GitHub Desktop.
Save ckothari/848b803d6650b1eacfd1d2d7de12cc14 to your computer and use it in GitHub Desktop.
Parallel-Cartesian Duality
<html>
<head>
<!--<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>-->
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.1/d3.js"></script>
<style>
#catesian-cont, #parallel-cont {
float: left;
}
#catesian-cont {
background-color: #0099CC;
margin-right: 20px;
border-right: 1px solid;
}
#parallel-cont {
background-color: #66CCFF;
}
</style>
</head>
<body>
<h1>Parallel-Cartesian Duality</h1>
<div id="catesian-cont">
<h3>Cartesian Coordinates</h3>
<div id="catesian"></div>
</div>
<div id="parallel-cont">
<h3>Parallel Coordinates</h3>
<div id="parallel"></div>
</div>
<script type="application/javascript">
var margin = {top: 20, right: 20, bottom: 30, left: 20},
width = 400 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var domain = [0, 10];
var idCounter = 0; //
var scaleX = d3.scaleLinear()
.domain(domain)
.range([0, width]),
scaleY = d3.scaleLinear()
.domain(domain)
.range([height, 0]);
window.scaleX = scaleX;
window.scaleY = scaleY;
var xAxis = d3.axisBottom(scaleX),
yAxis1 = d3.axisLeft(scaleY),
yAxis2 = d3.axisRight(scaleY);
var catSvg = d3.select('#catesian')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.on("click", click)
.append('g')
.attr('transform', "translate(" + margin.top + "," + margin.left+ ")");
var parSvg = d3.select('#parallel')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', "translate(" + margin.top + "," + margin.left+ ")");
catSvg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(' + 0 + "," + height + ")")
.call(xAxis);
catSvg.append('g')
.attr('class', 'y axis')
.call(yAxis1);
parSvg.append('g')
.attr('class', 'y axis')
.call(yAxis1);
parSvg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + width + "," + 0 + ")")
.call(yAxis2);
var lineGen = d3.line()
.x(function(d){
return d.x;
})
.y(function(d){
return scaleY(d.y);
});
function dragmoveCircle(d) {
var x = d3.event.x,
y = d3.event.y;
if( scaleX.invert(x) < domain[0] ||
scaleX.invert(x) > domain[1] ||
scaleY.invert(y) < domain[0] ||
scaleY.invert(y) > domain[1]
) return;
d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
d.datum([
{
x: 0,
y: scaleX.invert(d3.event.x)
},
{
x: width,
y: scaleY.invert(d3.event.y)
}
])
.attr('d', lineGen)
.attr('transform', 'translate(0,0)');
}
var dragmoveLine = function(d){
var ey = +d3.event.dy;
// get the old transform
var transform = d3.select(this).attr('transform') &&
d3.select(this).attr('transform').match(/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/);
if(transform) {
ey += +transform[2];
}
var node = d3.select(this).node(),
lineLength = node.getTotalLength(),
start = node.getPointAtLength(0),
end = node.getPointAtLength(lineLength);
var transformX = (scaleX(scaleY.invert(start.y + ey))),
transformY = (end.y + ey);
if( transformX < 0 ||
transformX > width ||
transformY < 0 ||
transformY > height
) return;
d3.select(this).attr("transform", function(d) {
return "translate(" + 0 + ", " + ey + ")";
});
var id = d3.select(this).attr('id');
id = id.substr(4, id.length);
d3.select('#dot' + id)
.attr('transform', 'translate('
+ transformX +
','
+ transformY +
')'
);
};
var resetHovers = function(){
d3.selectAll('.dot')
.style('fill', 'grey');
d3.selectAll('.par-line')
.style('stroke', 'grey');
};
var hoverPointHandler = function(){
resetHovers();
d3.select(this)
.style('fill', '#FF6600');
var id = d3.select(this).attr('id');
id = id.substr(4, id.length);
d3.select('#line-'+id)
.style("stroke", "#FF6600");
};
var hoverLineHandler = function(){
resetHovers();
d3.select(this)
.style("stroke", "#FF6600");
var id = d3.select(this).attr('id');
id = id.substr(5, id.length);
d3.select('#dot-'+id)
.style('fill', '#FF6600');
};
function click() {
// Ignore the click event if it was suppressed
if (d3.event.defaultPrevented) return;
var c = d3.mouse(this);
var x1 = c[0] - margin.left;
var x2 = c[1] - margin.top;
var point = catSvg.append("circle")
.style("stroke", "gray")
.style("fill", "gray")
.attr("transform", "translate(" + x1 + "," + x2 + ")")
.attr("r", 5)
.attr("class", "dot")
.attr("id", 'dot-' + idCounter)
.style("cursor", "pointer")
.call(d3.drag().on('drag', dragmoveCircle))
.on('mouseenter', hoverPointHandler)
.on('mouseleave', resetHovers);
// create a parallel coordinates system by specifying
// the axis-spacing vector
var d = [0, width];
var line = parSvg.append("path")
.datum([{x: 0, y: scaleX.invert(x1)}, {x: d[1], y: scaleY.invert(x2)}])
.attr('class', 'par-line')
.style("stroke", "gray")
.style("stroke-width", 2)
.style("cursor", "pointer")
.attr('d', lineGen)
.attr("id", 'line-' + idCounter)
.call(d3.drag().on('drag', dragmoveLine))
.on('mouseenter', hoverLineHandler)
.on('mouseleave', resetHovers);
// associate point with line to make it accessible in dragmove
point.data([line]);
// increment id counter
idCounter++;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment