Create a gist now

Instantly share code, notes, and snippets.

@1wheel /index.html
Last active Jun 29, 2016

Drag to move. Shift click and drag left/right to rotate.

Using multiple translates in the transform property didn't work:

transform(d){
  return [
    '  translate(', center,
    ') scale(', d.scale,
    ') rotate(', d.rotate,
    ') translate(', negPos(center), ')'
  ].join('')
}

So I multiplied several affine transformation matrices together to compose the transforms. Math in JS isn't super pretty:

var m = [
  a[0]*b[0] + a[2]*b[1], a[0]*b[2] + a[2]*b[3], a[0]*b[4] + a[2]*b[5] + a[4], 
  a[1]*b[0] + a[3]*b[1], a[1]*b[2] + a[3]*b[3], a[1]*b[4] + a[3]*b[5] + a[5], 
]

return [m[0], m[3], m[1], m[4], m[2], m[5]]

But things could always be worse.

Update: Robert Monfera pointed out that the above transform string was using the wrong variable to translate, the following does work:

transform(d){
  return [
    '  translate(', d.offset,
    ') translate(', d.centroid, ')',
    ') scale(', d.scale,
    ') rotate(', d.rotate*180,
    ') translate(', negPos(d.centroid), ')'
  ].join('')
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body{
background: black;
}
circle{
fill: green;
fill-opacity: .3;
stroke: white;
stroke-width: 1.5;
cursor: pointer;
}
path{
fill: none;
stroke: green;
}
</style>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src='script.js'></script>
function translate(sel, pos){
sel.attr('transform', function(d){
var posStr = typeof(pos) == 'function' ? pos(d) : pos
return 'translate(' + posStr + ')'
})
}
function ƒ(str){ return function(d){ return str ? d[str] : d } }
d3.selection.prototype.appendMany = function(data, name){
return this.selectAll(name).data(data).enter().append(name)
}
var width = 960,
height = 500;
var svg = d3.select('html')
.append('svg')
.attr('width', width)
.attr('height', height)
var g = svg.append('g')
var path = svg.append('path')
var circles = [
[100, 300],
[400, 100],
[800, 200],
]
var drag = d3.behavior.drag()
.on('dragstart', function(d){})
.on('drag', function(d){
d[0] = d3.event.x
d[1] = d3.event.y
d3.select(this).call(translate, ƒ())
calcPath()
})
.origin(function(d){ return {x: d[0], y: d[1]} })
var circleSel = svg.appendMany(circles, 'circle')
.attr('r', 10)
.call(translate, ƒ())
.call(drag)
function calcPath(){
var a = circles[0]
var b = circles[2]
var c = circles[1]
var A = dist(b, c)
var B = dist(c, a)
var C = dist(a, b)
var angle = Math.acos((A*A + B*B - C*C)/(2*A*B))
//calc radius of circle
var K = .5*A*B*Math.sin(angle)
var r = A*B*C/4/K
//large arc flag
var laf = +(Math.PI/2 > angle)
//sweep flag
var saf = +((b[0] - a[0])*(c[1] - a[1]) - (b[1] - a[1])*(c[0] - a[0]) < 0)
path.attr('d', ['M', a, 'A', r, r, 0, laf, saf, b].join(' '))
}
function parsePath(str){
var path = svg.append('path').attr('d', str)
var l = path.node().getTotalLength()
circles = [0, .5, 1].map(function(d){
var p = path.node().getPointAtLength(d*l)
console.log(d, p)
return [p.x, p.y]
})
circleSel.data(circles).call(translate, ƒ())
calcPath()
}
parsePath('M 359,317 A 170.70008627865235 170.70008627865235 0 1 0 146.9999542236328,382')
function slope(a, b){
return (a[1] - b[1])/(a[0] + b[0])
}
function mid(a, b){
return [(a[0] + b[0])/2, (a[1] + b[1])/2]
}
function dist(a, b){
return Math.sqrt(
Math.pow(a[0] - b[0], 2) +
Math.pow(a[1] - b[1], 2))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment