已知,三角形的三個頂點座標,求三角形的外接圓和內接圓。
三角形有重心,垂心,內心,外心,旁心。
外心即外接圓的圓心,是三角形三邊垂直平分線的交點
繪製外接圓相對簡單一點,
已知,兩條直線方程的一般式:
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>