Skip to content

Instantly share code, notes, and snippets.

@mgold
Last active February 2, 2016 15:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mgold/295a5c88f82d80643bcf to your computer and use it in GitHub Desktop.
Save mgold/295a5c88f82d80643bcf to your computer and use it in GitHub Desktop.
Triangle Centers

Different ways to find the "center" of a triangle. Inspired by Numberphile. Grab and move the vertices of the large triangle.

  • The centroid is found by connecting each angle with the midpoint of the opposite side. All three such lines meet at a single point.

  • The circumcenter is is the intersection of the perpendicular bisectors of each side. Therefore it is equidistant from the three vertices, and is the center of the circle defined by them.

  • The orthocenter is defined by dropping a vertical from each angle down to the opposite side (extending the side if necessary). The vertical does not usually intersect at the midpoint. Once again, all three such lines intersect at one point.

  • The incenter is defined by bisecting each angle with a line continued to the opposite side.

The orthocenter is twice as far from the centroid as the circumcenter, and all three lie on a single line known as the Euler line. Of these three, only the centroid always lies inside the triangle. The incenter does not usually lie on this line, except for isoceles triangles. For equilateral triangles, all four centers are the same point.

Triangle centers demonstrate that mathematics isn't always about a single right answer, but rather, many good ideas can coexist. It also demonstrates how different geometric notions can be brought to bear on a problem and be distinct, rather than be different representations of the same thing.

Built with blockbuilder.org

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg { width:100%; height: 100% }
text { text-anchor: middle; font-family: avenir, sans-serif}
path.triangle { fill: none; stroke: black; stroke-width: 2px; shape-rendering: geometricPrecision;}
circle.grabme { cursor: pointer}
line.centroid { stroke: #FF4136 }
circle.centroid, text.centroid { fill: #FF4136 }
line.circumcenter { stroke: #2ECC40 }
circle.circumcenter, text.circumcenter { fill: #2ECC40 }
line.orthocenter { stroke: #0074D9 }
circle.orthocenter, text.orthocenter { fill: #0074D9 }
line.incenter { stroke: #FFDC00 }
circle.incenter, text.incenter { fill: #FFDC00 }
line.euler { stroke: #AAAAAA; stroke-width: 4px}
</style>
</head>
<body>
<script>
var width = 960;
var height = 500;
var centerNames = ["Centroid", "Circumcenter", "Orthocenter", "Incenter"];
var centerWidth = width/centerNames.length;
var centerHeight = height/3;
// The only piece of mutable state
var points = [{x:-3.5, y:1}, {x:7, y:4}, {x:0.84,y:-4}];
var lineGen = d3.svg.line();
function line(scale){
return lineGen(points.map(function(p){return [p.x*scale, p.y*scale]}))+"Z";
}
var svg = d3.select("body").append("svg")
.attr({width: width, height: height})
var panels = svg.selectAll("nonexistant")
.data(centerNames)
.enter()
.append("g")
.attr("transform", function(d,i){
return "translate("+(i+0.5)*centerWidth+","+centerHeight/2+")"
})
panels.append("text")
.text(function(d){return d})
.attr("dy", centerHeight/2 + "px")
.attr("class", function(d){ return d.toLowerCase()})
panels.append("path")
.attr("class", "triangle")
.datum(15)
panels.each(function(d,i){
var center = d3.select(this);
center.selectAll("line")
.data(d3.range(3))
.enter()
.append("line")
.attr("class", d.toLowerCase())
})
var main = svg.append("g")
.attr("transform", "translate("+width/2+","+2*centerHeight+")")
main.append("line")
.attr("class", "euler")
main.append("path")
.attr("class", "triangle")
.datum(25)
main.selectAll("circle.center")
.data(centerNames)
.enter()
.append("circle")
.attr("class", function(d){ return "center " + d.toLowerCase()})
.attr("r", 4)
var drag = d3.behavior.drag()
.origin(function(d){return {x:points[d].x*25, y:points[d].y*25}})
.on("drag", function(d){
points[d] = {x:d3.event.x/25, y:d3.event.y/25}
update();
})
var handles = main.selectAll("nonexistant")
.data(d3.range(3))
.enter()
.append("circle")
.attr("class", "grabme")
.attr("r", 4)
.call(drag)
function midpoint(p1, p2){
var dx = p1.x + p2.x;
var dy = p1.y + p2.y;
return {x: dx/2, y: dy/2}
}
function slope(p1,p2){
return (p2.y - p1.y) / (p2.x - p1.x);
}
function perpendicular(m){
return -1/m;
}
function intersect(p1, m1, p2, m2){
var j = (m1*(p1.x - p2.x) - p1.y + p2.y) / (m1 - m2);
return {x: p2.x + j, y: p2.y + j*m2};
}
function distance2(p1, p2){
var dx = p1.x - p2.x;
var dy = p1.y - p2.y;
return dx*dx + dy*dy;
}
function angle (p1, p2, p3){
var angle1 = Math.atan2(p1.y - p2.y, p1.x - p2.x)
var angle3 = Math.atan2(p3.y - p2.y, p3.x - p2.x)
var delta = angle1 - angle3;
if (delta < 0){
delta += 2*Math.PI
}
return delta || 0;
}
function makeLine(line, p1, p2){
line
.attr("x1", p1.x*15)
.attr("y1", p1.y*15)
.attr("x2", p2.x*15)
.attr("y2", p2.y*15)
}
var invalidPoint = {x: 9999, y: 9999}
function update(){
var centers = [];
d3.selectAll(".triangle")
.attr("d", line);
d3.selectAll("line.centroid")
.each(function(d){
var p1 = points[d];
var mp1 = midpoint(points[(d+1)%3], points[(d+2)%3])
d3.select(this).call(makeLine, p1, mp1)
if (d == 0){
var m1 = slope(p1, mp1);
var p2 = points[1];
var m2 = slope(p2, midpoint(p1, points[2]));
centers.push(intersect(p1, m1, p2, m2))
}
})
d3.selectAll("line.circumcenter")
.each(function(d){
var p1 = points[d];
var mp1 = midpoint(p1, points[(d+1)%3]);
if (d == 0){
var p2 = points[1];
var p3 = points[2];
var m1 = perpendicular(slope(p1, p2));
var mp2 = midpoint(p2, p3);
var m2 = perpendicular(slope(p2, p3));
centers.push(intersect(mp1, m1, mp2, m2))
}
d3.select(this).call(makeLine, mp1, centers[1])
})
d3.selectAll("line.orthocenter")
.each(function(d){
var p1 = points[d];
var p2 = points[(d+1)%3]
var m2 = slope(p2, points[(d+2)%3]);
var m1 = perpendicular(m2);
var base = intersect(p1, m1, p2, m2);
if (d == 0){
var p3 = points[2]
var m4 = slope(p3, p1);
var m3 = perpendicular(m4);
var otherBase = intersect(p2, m3, p3, m4)
centers.push(intersect(p1, slope(p1, base),
p2, slope(p2, otherBase)));
}
if (distance2(p1, base) > distance2(p1, centers[2]))
d3.select(this).call(makeLine, p1, base)
else
d3.select(this).call(makeLine, p1, centers[2])
})
var stashP, stashM;
d3.selectAll("line.incenter")
.each(function(d){
var p0 = points[(d+2)%3];
var p1 = points[d];
var p2 = points[(d+1)%3];
var d_theta = angle(p0, p1, p2) / 2;
var baseTheta = Math.atan2(p0.y - p1.y, p0.x - p1.x);
var theta = baseTheta - d_theta;
var m = Math.tan(theta);
var farPoint = intersect(p1, m, p0, slope(p0, p2));
if (d==0){
stashP = p1;
stashM = m;
}else if (d==1){
var ic = intersect(stashP, stashM, p1, m);
if (isNaN(ic.x) || isNaN(ic.y)) ic = invalidPoint;
centers.push(ic)
}
d3.select(this).call(makeLine, p1, farPoint);
})
main.selectAll("circle.center")
.attr("cx", function(d,i){ return centers[i].x*25})
.attr("cy", function(d,i){ return centers[i].y*25})
handles
.attr("cx", function(d,i){return points[i].x*25})
.attr("cy", function(d,i){return points[i].y*25})
main.select("line.euler")
.attr("x1", centers[1].x*25)
.attr("y1", centers[1].y*25)
.attr("x2", centers[2].x*25)
.attr("y2", centers[2].y*25)
}
update();
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment