block by syntagmatic be8287ae3724e0c83c31273b20710496

Outer Tangent

Full Screen

Calculating the outer tangent of two circles using this solution on Stackoverflow by Markus Jarderot.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.circle {
  fill-opacity: .1;
  fill: #000;
  stroke-opacity: .5;
  stroke: #000;
  stroke-width: 2px;
}

line {
  stroke-opacity: .8;
  stroke: #0b3;
  stroke-width: 2px;
}

</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>

// Based on //stackoverflow.com/a/12037737
function outerTangent(x1, y1, r1, x2, y2, r2) {
  var dx = x2 - x1;
  var dy = y2 - y1;
  var dist = Math.sqrt(dx*dx + dy*dy);

  if (dist <= Math.abs(r2-r1)) return;   // no valid tangents

  // Rotation from x-axis
  var angle1 = Math.atan2(dy,dx);
  var angle2 = Math.acos((r1 - r2)/dist);

  return [
    [
      [x1 + r1 * Math.cos(angle1 + angle2), y1 + r1 * Math.sin(angle1 + angle2)],
      [x2 + r2 * Math.cos(angle1 + angle2), y2 + r2 * Math.sin(angle1 + angle2)]
    ],
    [
      [x1 + r1 * Math.cos(angle1 - angle2), y1 + r1 * Math.sin(angle1 - angle2)],
      [x2 + r2 * Math.cos(angle1 - angle2), y2 + r2 * Math.sin(angle1 - angle2)]
    ]
  ];
}

var c1 = {x: 180, y: 150, r: 10},
    c2 = {x: 500, y: 200, r: 120};

var color = d3.scale.category10();

var svg = d3.select("svg");

var circle = svg.selectAll(".circle")
    .data([c1, c2])
  .enter().append("g")
    .attr("class", "circle")
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
    .call(d3.behavior.drag()
        .origin(function(d) { return d; })
        .on("dragstart", dragstarted)
        .on("drag", dragged));

circle.append("circle")
    .attr("r", function(d) { return d.r; });

var tangent = svg.selectAll(".line")
    .data([
      function() { return outerTangent(c1.x, c1.y, +c1.r, c2.x, c2.y, +c2.r); },
    ])
  .enter().insert("g", ".line")
    .attr("class", "line")
    .style("stroke", function(d, i) { return color(i); });

tangent.append("line")
    .attr("class", "line-1");

tangent.append("line")
    .attr("class", "line-2");

update();

function dragstarted(d) {
  this.parentNode.appendChild(this);
}

function dragged(d) {
  d3.select(this).attr("transform", "translate(" + (d.x = d3.event.x) + "," + (d.y = d3.event.y) + ")");
  update();
}

function update() {
  tangent.each(function(f) {
    var c = f();
    if (c) {
      d3.select(this).style("display", null);
      tangent.select(".line-1")
        .attr("x1", c[0][0][0])
        .attr("x2", c[0][1][0])
        .attr("y1", c[0][0][1])
        .attr("y2", c[0][1][1]);
      tangent.select(".line-2")
        .attr("x1", c[1][0][0])
        .attr("x2", c[1][1][0])
        .attr("y1", c[1][0][1])
        .attr("y2", c[1][1][1]);
    } else {
      d3.select(this).style("display", "none");
    }
  });
}

</script>