Calculating the outer tangent of two circles using this solution on Stackoverflow by Markus Jarderot.
| <!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 http://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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment