倾斜角的概念
Math的静态方法atan2可求出从原点引出的一条射线相对于X轴正半轴的夹角。
let point = Point(100, 50);
let slopeRadian = Math.atan2(point.y, point.x);
let degree = 180 / Math.PI * slopeRadian;
需注意的是,atan2的两个参数分别是y及x,而不是x及y。
所返回的slopeRadian是表示射线倾斜度的一个角度,用弧度制表示,是这条射线相对于X轴正半轴的夹角。我们称之为倾斜角(slope angle)。上面将其换为角度制。其在笛卡尔坐标系中的示意图如下:
ctx.translate(canvas.clientWidth / 2, canvas.clientHeight / 2);
ctx.scale(1, -1);
let point = Point(100, 50);
let slope = Math.atan2(point.y, point.x);
let degree = slope * 180 / Math.PI;
drawAxis();
drawLine(Point(0, 0), point);
drawLabel(`Point(${point.x}, ${point.y})`, point.x + 20, point.y + 20, 'yellow');
drawIntersectAngle(point, 20, degree);
即,在笛卡尔坐标系上,从原点到点(100, 50)的射线,其与X轴正半轴的夹角为26o。
解斜截式直线公式方程
根据上节,对于任意两点所组成的直线,我们能求出其倾斜角,进而能求出其斜率来:
let ptSrc = Point(10, 10);
let ptDst = Point(50, 30);
let slopeAngle = Math.atan2(ptDst.y - ptSrc.y, ptDst.x - ptSrc.x);
let k = Math.tan(slopeAngle);
则斜截式直线公式中的截距b为:
let ptSrc = Point(10, 10);
let ptDst = Point(50, 30);
let slopeAngle = Math.atan2(ptDst.y - ptSrc.y, ptDst.x - ptSrc.x);
let k = Math.tan(slopeAngle);
let b = y - k * x;
代入两点中任意一点的x及y,即可求出已知两点射线中的b。
let ptSrc = Point(10, 10);
let ptDst = Point(50, 30);
let slopeAngle = Math.atan2(ptDst.y - ptSrc.y, ptDst.x - ptSrc.x);
let k = Math.tan(slopeAngle);
let b = ptDst.y - k * ptDst.x;
绘图:
ctx.translate(canvas.clientWidth / 2, canvas.clientHeight / 2);
ctx.scale(1, -1);
let ptSrc = Point(-30, -10);
let ptDst = Point(40, 50);
let slopeAngle = Math.atan2(ptDst.y - ptSrc.y, ptDst.x - ptSrc.x);
let k = Math.tan(slopeAngle);
let b = ptDst.y - k * ptDst.x;
console.log(k, b);
drawAxis();
drawLine(ptSrc, ptDst);
修改上面ptSrc及ptDst的坐标值,观察Console面板中k与b的值的变化。
求直线上任意一点的坐标
再回看斜截式直线公式:
y = kx + b
k与b均已求出,只剩下自变量y与x了。这意味着在该线段中的任意一点,只要我们知道其x或y中的任意一个数值,我们就能求出该点的坐标来。
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min);
}
function getRandomPointOnLineSegment(ptSrc, ptDst) {
let slopeAngle = Math.atan2(ptDst.y - ptSrc.y, ptDst.x - ptSrc.x);
let k = Math.tan(slopeAngle);
let b = ptDst.y - k * ptDst.x;
let x = getRandomIntInclusive(Math.min(ptSrc.x, ptDst.x), Math.max(ptSrc.x, ptDst.x));
let y = k * x + b;
return Point(x, y);
}
效果如下:
ctx.translate(canvas.clientWidth / 2, canvas.clientHeight / 2);
ctx.scale(1, -1);
let ptSrc = Point(-80, -10);
let ptDst = Point(40, 50);
requestAnimationFrame(initFirstFrame);
function doInAnimate(currAngle) {
drawAxis();
let point = getRandomPointOnLineSegment(ptSrc, ptDst);
drawCircle(point, 5);
drawLine(ptSrc, ptDst);
}
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min);
}
function getRandomPointOnLineSegment(ptSrc, ptDst) {
let slopeAngle = Math.atan2(ptDst.y - ptSrc.y, ptDst.x - ptSrc.x);
let k = Math.tan(slopeAngle);
let b = ptDst.y - k * ptDst.x;
let x = getRandomIntInclusive(Math.min(ptSrc.x, ptDst.x), Math.max(ptSrc.x, ptDst.x));
let y = k * x + b;
return Point(x, y);
}
上面,我们先随机取出线段中任意一点的x值,然后再求出该点对应的y值。
函数getRandomPointOnLineSegment的意义:已知任意两点坐标,我们能求出这两点所组成的线段中任意一点的坐标。
当然,如果不要求限定于线段中的点,则可以扩展为取出直线上任意一点的坐标:
ctx.translate(canvas.clientWidth / 2, canvas.clientHeight / 2);
ctx.scale(1, -1);
let ptSrc = Point(-80, -10);
let ptDst = Point(40, 50);
requestAnimationFrame(initFirstFrame);
function doInAnimate(currAngle) {
drawAxis();
let point = getRandomPointOnLine(ptSrc, ptDst);
drawCircle(point, 5);
drawLine(ptSrc, ptDst);
}
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min);
}
function getRandomPointOnLine(ptSrc, ptDst) {
let slopeAngle = Math.atan2(ptDst.y - ptSrc.y, ptDst.x - ptSrc.x);
let k = Math.tan(slopeAngle);
let b = ptDst.y - k * ptDst.x;
let x = getRandomIntInclusive(-canvas.clientWidth / 2, canvas.clientWidth / 2);
let y = k * x + b;
return Point(x, y);
}
函数getRandomPointOnLine的意义:已知任意两点坐标,我们能求出这两点所组成的直线上任意一点的坐标。