已知,三角形的三个顶点坐标,求三角形的外接圆和内接圆。
三角形有重心,垂心,内心,外心,旁心。
外心即外接圆的圆心,是三角形三边垂直平分线的交点
绘制外接圆相对简单一点,
已知,两条直线方程的一般式:
a1 * x + b1 * y + c1 = 0
a2 * x + b2 * y + c2 = 0
则,交点坐标为
x = (b2 * c1 - b1 * c2) / (a2 * b1 - a1 * b2) (1)
y = (a1 * c2 - a2 * c1) / (a2 * b1 - a1 * b2) (2)
如果是两条直线方程的点斜式:
(y - y1) / (x - x1) = k1
(y - y2) / (x - x2) = k2
先把点斜式转为一般式,可得,
a1 = k1, b1 = -1, c1 = y1 - k1 * x1
a2 = k2, b2 = -1, c2 = y2 - k2 * x2
代入(1),(2) 得到,
x = (y2 - k2 * x2 - y1 + k1 * x1) / (k1 - k2) (3)
y = (k1 * y2 - k2 * y1 - k1 * k2 * (x2 - x1)) / (k1 - k2) (4)
有了公式(3)和(4), 就很好找到外心的坐标了。三角形边的中点坐标很容易得到吧。一条的斜率也很好得到。
设三角形,三个点的坐标为A(x1, y1), B (x2, y2), C (x3, y3)
边AB的中点坐标为( (x1 + x2)/2, (y1 + y2)/2 ), 斜率为 (y2 - y1) / (x2 - x1),
边AB的法线与AB垂直,法线的斜率为 (x1 - x2) / (y2 - y1)
外接圆的半径为圆心到顶点的距离
内心即内接圆的圆心,是三角形三角角平分线的交点,找内心的坐标相对麻烦,关键在于如找到角平分线的斜率,找到斜率后就可以应用点斜式, 公式(3)和(4) 得到内心的坐标
角平分线的斜率,可以这样来找到,比如求A角的角平分线斜率,先找到向量(A到B)与x轴的夹角,再找到
向量(A到C)与x轴的夹角,两个夹角的和除以2 就是角平分线与x轴的夹角
内接圆的半径需要用到,点到直线的距离公式:
公式描述:公式中的直线方程为Ax+By+C=0,点P的坐标为(x0,y0)。
代码说明:
function getAngleWithX_axis(v1, v2)
v1到v2的向量与x轴的夹角,返回的角度是弧度制
用反余弦函数来求夹角,因为用反余弦函数实现起来,逻辑最清晰简单,只需要判断v2.y >= v1.y
function getMiddleSlope(a, b, c)
返回角cba的角平分线的斜率, 返回的角度是弧度制
function getCrossPoint(k1, v1, k2, v2)
已知,两条直线方程的点斜率式,得到它们的交点坐标,公式(3)和(4) 的应用
function getPointToLineDistance(p, start, end)
已知,直线上的两个点start和end,线外的一点p, 返回点p到直线的距离
<html><head><title>Incircle and circumcircle</title>
</head><body><div>
<canvas id="canvas" width="600" height="450" style="border: solid black 1px; cursor: default;"></canvas>
</div>
<div>
<input type="button" value="clear" onclick="demo.clear();">
<input type="button" value="generate" onclick="demo.generate(1);">
</div>
<script type="text/javascript">
function getRandom(min, max) {
var d = max - min;
return min + Math.random() * d;
}
function getDistance(v1, v2) {
var dx = v2.x - v1.x;
var dy = v2.y - v1.y;
return Math.sqrt(dx * dx + dy * dy);
}
function getAngleWithX_axis(v1, v2) {
var distance = getDistance(v1, v2);
var dx = v2.x - v1.x;
var cosA = dx / distance;
if (v2.y >= v1.y) {
return Math.acos(cosA);
} else {
return 2 * Math.PI - Math.acos(cosA);
}
}
function getMiddleSlope(a, b, c) {
var a1 = getAngleWithX_axis(b, a);
var a2 = getAngleWithX_axis(b, c);
var a3 = (a1 + a2) / 2;
return Math.tan(a3);
}
function getCrossPoint(k1, v1, k2, v2) {
var cross_x = (v2.y - k2 * v2.x - v1.y + k1 * v1.x) / (k1 - k2);
var cross_y = (k1 * v2.y - k2 * v1.y - k1 * k2 * (v2.x - v1.x)) / (k1 - k2);
return new Vertex(cross_x, cross_y);
}
function getPointToLineDistance(p, start, end) {
var a = end.y - start.y;
var b = start.x - end.x;
var c = end.x * start.y - start.x * end.y;
var d = Math.abs(a * p.x + b * p.y + c) / Math.sqrt(a*a + b*b);
return d;
}
function Demo() {
var vertices = [];
var circumcircleCenter;
var circumcircleRadius;
var incircleCenter;
var incircleRadius;
var barycenter;
this.generate = function(n) {
var v1 = new Vertex(getRandom(100, 200), getRandom(100, 400));
var v2 = new Vertex(getRandom(200, 400), getRandom(100, 400));
var v3 = new Vertex(getRandom(150, 350), getRandom(100, 400));
vertices.push(v1, v2, v3);
// var kv1 = (v1.y - v2.y) / (v1.x - v2.x);
// var kv2 = (v2.y - v3.y) / (v2.x - v3.x);
var k12 = (v2.x - v1.x) / (v1.y - v2.y);
var k23 = (v3.x - v2.x) / (v2.y - v3.y);
var m12 = new Vertex((v1.x + v2.x)/2, (v1.y + v2.y)/2);
var m23 = new Vertex((v2.x + v3.x)/2, (v2.y + v3.y)/2);
// var circumcircle_x = (m23.y - k23 * m23.x - m12.y + k12 * m12.x) / (k12 - k23);
// var circumcircle_y = (k12 * m23.y - k23 * m12.y - k12 * k23 * (m23.x - m12.x)) / (k12 - k23);
// circumcircleCenter = new Vertex(circumcircle_x, circumcircle_y);
circumcircleCenter = getCrossPoint(k12, m12, k23, m23);
circumcircleRadius = getDistance(circumcircleCenter, v1);
var k213_m = getMiddleSlope(v2, v1, v3);
var k123_m = getMiddleSlope(v1, v2, v3);
incircleCenter = getCrossPoint(k213_m, v1, k123_m, v2);
incircleRadius = getPointToLineDistance(incircleCenter, v1, v2);
var centroid = new Vertex(0,0);
centroid.x = (v1.x + v2.x + v3.x)/3;
centroid.y = (v1.y + v2.y + v3.y)/3;
var a123 = getAngleWithX_axis(v1, m23);
var a312 = getAngleWithX_axis(v3, m12);
var k123 = Math.tan(a123), k312 = Math.tan(a312);
barycenter = getCrossPoint(k123, v1, k312, v3);
console.log("centroid: " + centroid + ", barycenter: " + barycenter + ", incircleCenter: " + incircleCenter);
this.render();
vertices = [];
};
this.clear = function() {
vertices = [];
this.render();
}
// function render() {
this.render = function() {
var canvas = document.getElementById('canvas');
if (!canvas.getContext)
return;
var context = canvas.getContext('2d');
context.clearRect(0, 0, Number(canvas.width), Number(canvas.height));
if (vertices.length <= 0) {
return;
}
vertices.forEach(function(vertex) {
context.beginPath();
context.arc(vertex.x, vertex.y, 5, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = "#0000ff";
context.fill();
});
context.beginPath();
context.moveTo(vertices[0].x, vertices[0].y);
for (var i = 1; i < vertices.length; i++) {
context.lineTo(vertices[i].x, vertices[i].y);
}
context.lineTo(vertices[0].x, vertices[0].y);
context.closePath();
context.strokeStyle = "#ff0000";
context.stroke();
//draw circumcircle
drawCircle(context, circumcircleCenter, circumcircleRadius, "#006600");
//draw incircle
drawCircle(context, incircleCenter, incircleRadius, "#990066");
}
};
function drawCircle(ctx, center, radius, centerColor) {
ctx.beginPath();
ctx.arc(center.x, center.y, 5, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = centerColor;
ctx.fill();
ctx.beginPath();
ctx.arc(center.x, center.y, radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.strokeStyle = "#ff0000";
ctx.stroke();
}
function Vertex(x, y) {
this.x = x;
this.y = y;
}
var demo = new Demo();
window.onload = function() {
document.getElementById("canvas").onclick = function(e) {
e = e ? e : window.event;
var rect = this.getBoundingClientRect();
demo.addAt(e.clientX - rect.left, e.clientY - rect.top);
}
};
</script>
</body></html>