如何解决如何随着形状的旋转和位置的变化移动点
我有一个网络应用程序,我在其中绘制一个三角形并在其顶部绘制点以显示它所具有的顶点。 (图 1 在 0 弧度处)
圆形和三角形旋转得很好,只是我似乎无法适当移动的蓝点。当我旋转三角形(连同圆)时,除了红点之外,X 和 Y 不会转换到三角形的任何一个角。 (图 2 在 0.75 弧度处)
整个形状是用以下顶点作为显示点绘制的。
this.transform = ctx.getTransform();
this.boundPoints[0] = { //red point
x: (this.transform.a + this.x)+(this.radius)* Math.cos(this.rotation),y: (this.transform.d + this.y)+(this.radius)* Math.sin(this.rotation)
}
this.boundPoints[1] = { //blue point
x: (this.transform.a + this.x)+(this.radius+ this.range)* Math.cos(this.rotation),y: (this.transform.d + this.y)+(this.radius)* Math.sin(this.rotation)
}
无论它在画布中的位置和旋转如何,该点都保持其相对于三角形的位置。不旋转,我可以把它保持在那里,它的 Y 是
y: (this.transform.d + this.y+this.range)
但现在我无法旋转或移动形状而点不会丢失其位置。 (注意:this.rotation 是以弧度为单位的角度)
解决方法
我跟踪任何形状的所有点的方法是在我的类中创建任何数组,将这些值与您要绘制的实际点分开存储。我将这些存储点主要用于具有已变换/旋转/缩放的奇怪形状的碰撞检测。
如果没有您的代码,很难看出我将如何实现此技术,但这里有一个旋转三角形示例,您可以对其进行缩放和变换,并且始终跟踪点。这个例子还包括一段注释掉的代码,展示了如何使用质心从中心旋转。
this.position
是翻译
this.size
是比例
this.r
是旋转的
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 300;
canvas.height = 300;
let ptACopy,ptBCopy,ptCCopy;
class Triangle {
constructor(ptA,ptB,ptC) {
this.type = "tri";
this.ptA = ptACopy = ptA;
this.ptB = ptBCopy = ptB;
this.ptC = ptCCopy = ptC;
this.position = { x: 100,y: 100 }; //use this to position
this.size = { x: 2,y: 1 };
this.centroid = {
ox: (this.ptA.x + this.ptB.x + this.ptC.x) / 3,oy: (this.ptA.y + this.ptB.y + this.ptC.y) / 3
};
this.c = "red";
this.a = 0;
this.r = this.a * (Math.PI / 180);
this.points = [];
for (let i = 0; i < 3; i++) {
this.points.push({ x: 0,y: 0 });
}
}
draw() {
//updates the points to counter the translating of the canvas to the centroid
//this is used to rotate from center if wanted
/*this.ptA = {
x: ptACopy.x - this.centroid.ox,y: ptACopy.y - this.centroid.oy
};
this.ptB = {
x: ptBCopy.x - this.centroid.ox,y: ptBCopy.y - this.centroid.oy
};
this.ptC = {
x: ptCCopy.x - this.centroid.ox,y: ptCCopy.y - this.centroid.oy
};*/
let cos = Math.cos(this.r);
let sin = Math.sin(this.r);
ctx.save();
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.setTransform(cos * this.size.x,sin * this.size.x,-sin * this.size.y,cos * this.size.y,this.position.x,this.position.y);
ctx.moveTo(this.ptA.x,this.ptA.y);
ctx.lineTo(this.ptB.x,this.ptB.y);
ctx.lineTo(this.ptC.x,this.ptC.y);
ctx.lineTo(this.ptA.x,this.ptA.y);
ctx.fill();
ctx.closePath();
ctx.restore();
}
updateCorners() {
this.a += 0.1;
this.r = this.a * (Math.PI / 180);
let cos = Math.cos(this.r);
let sin = Math.sin(this.r);
this.points[0].x =
this.ptA.x * this.size.x * cos -
this.ptA.y * this.size.y * sin +
this.position.x;
this.points[0].y =
this.ptA.x * this.size.x * sin +
this.ptA.y * this.size.y * cos +
this.position.y;
this.points[1].x =
this.ptB.x * this.size.x * cos -
this.ptB.y * this.size.y * sin +
this.position.x;
this.points[1].y =
this.ptB.x * this.size.x * sin +
this.ptB.y * this.size.y * cos +
this.position.y;
this.points[2].x =
this.ptC.x * this.size.x * cos -
this.ptC.y * this.size.y * sin +
this.position.x;
this.points[2].y =
this.ptC.x * this.size.x * sin +
this.ptC.y * this.size.y * cos +
this.position.y;
}
drawPoints() {
ctx.fillStyle = "blue";
this.points.map((x) => {
ctx.beginPath();
ctx.arc(x.x,x.y,3,Math.PI * 2);
ctx.fill();
});
}
}
let triangle = new Triangle(
{ x: 10,y: 20 },{ x: 50,y: 60 },{ x: 30,y: 100 }
);
function animate() {
ctx.clearRect(0,canvas.width,canvas.height);
triangle.draw();
triangle.updateCorners();
triangle.drawPoints();
requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>
澄清代码中的一些事情: 使用以下代码的目的是为了确保在翻译时准确绘制形状。
this.ptA = {
x: ptACopy.x - this.centroid.ox,y: ptACopy.y - this.centroid.oy
};
如果不复制点并尝试做
this.ptA = {
x: ptA.x - this.centroid.ox,y: ptA.y - this.centroid.oy
};
然后我会得到未定义,因为我正在尝试使用 ptA 来计算 ptA。
此外,在创建三角形时,如果我希望 ptA 位于 (0,0) 处,那么我可以将其设置在那里,然后使用上述函数偏移三角形以进行旋转。使用它让它围绕 ptA 旋转 20px 半径的示例:
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 300;
canvas.height = 300;
let ptACopy,y: 100 }; //use this to position
this.size = { x: 1,y: 0 });
}
}
draw() {
//updates the points to counter the translating of the canvas to the centroid
this.ptA = {
x: ptACopy.x + 20,y: ptACopy.y + 20
};
this.ptB = {
x: ptBCopy.x + 20,y: ptBCopy.y + 20
};
this.ptC = {
x: ptCCopy.x + 20,y: ptCCopy.y + 20
};
let cos = Math.cos(this.r);
let sin = Math.sin(this.r);
ctx.save();
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.setTransform(cos * this.size.x,this.ptA.y);
ctx.fill();
ctx.closePath();
ctx.fillStyle = 'rgba(0,0.2)';
ctx.fillRect(0,canvas.height);
ctx.restore();
}
updateCorners() {
this.a += 0.5;
this.r = this.a * (Math.PI / 180);
let cos = Math.cos(this.r);
let sin = Math.sin(this.r);
this.points[0].x =
this.ptA.x * this.size.x * cos -
this.ptA.y * this.size.y * sin +
this.position.x;
this.points[0].y =
this.ptA.x * this.size.x * sin +
this.ptA.y * this.size.y * cos +
this.position.y;
this.points[1].x =
this.ptB.x * this.size.x * cos -
this.ptB.y * this.size.y * sin +
this.position.x;
this.points[1].y =
this.ptB.x * this.size.x * sin +
this.ptB.y * this.size.y * cos +
this.position.y;
this.points[2].x =
this.ptC.x * this.size.x * cos -
this.ptC.y * this.size.y * sin +
this.position.x;
this.points[2].y =
this.ptC.x * this.size.x * sin +
this.ptC.y * this.size.y * cos +
this.position.y;
}
drawPoints() {
ctx.fillStyle = "blue";
this.points.map((x) => {
ctx.beginPath();
ctx.arc(x.x,Math.PI * 2);
ctx.fill();
});
}
}
let triangle = new Triangle(
{ x: 0,y: 0 },canvas.height);
triangle.draw();
triangle.updateCorners();
triangle.drawPoints();
requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>
我为画布设置了背景以实现更好的可视化。
更新:
我正在添加一个使用 getTransform()
返回变换矩阵的示例。
然后我们可以使用这些值通过将它们传递给函数来计算每个点。这会稍微缩短类中的代码并使 IMO 看起来更简洁。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 400;
let t;
class Rect {
constructor(x,y,w,h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.scale = {x: 1,y: 1}
this.cx = this.x + this.w / 2;
this.cy = this.y + this.h / 2;
this.color = "red";
this.angle = 0;
this.rotation = (this.angle * Math.PI) / 180;
this.pts = []
}
draw() {
this.angle += 0.5;
this.rotation = (this.angle * Math.PI) / 180;
const cos = Math.cos(this.rotation)
const sin = Math.sin(this.rotation)
ctx.save();
ctx.setTransform(cos * this.scale.x,sin * this.scale.x,-sin * this.scale.y,cos * this.scale.y,this.x,this.y);
t = ctx.getTransform();
ctx.fillStyle = this.color;
ctx.fillRect(-this.w / 2,-this.h / 2,this.w,this.h);
ctx.restore();
}
drawVertices() {
for (let i=0; i < this.pts.length; i++) {
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.arc(this.pts[i].x,this.pts[i].y,Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
updateVertices() {
this.pts[0] = calcVertices(t['a'],t['b'],t['c'],t['d'],t['e'],t['f'],this.cx,this.cy)//top left width and height are passed as 0.
this.pts[1] = calcVertices(t['a'],this.cy) //top right only passes width. Height is 0.
this.pts[2] = calcVertices(t['a'],this.h,this.cy) //bottom right passes both wodth and height.
this.pts[3] = calcVertices(t['a'],this.cy)//bottom left only passes height. Width is 0.
}
}
let rect1 = new Rect(100,100,50,75);
let rect2 = new Rect(250,150,25);
function calcVertices(a,b,c,d,e,f,h,cx,cy) {
let x,y;
x = (e + w - cx) * a + (f + h - cy) * c + (e);
y = (e + w - cx) * b + (f + h - cy) * d + (f);
return {x: x,y: y}
}
function animate() {
ctx.clearRect(0,canvas.height);
rect1.draw();
rect1.updateVertices();
rect1.drawVertices();
rect2.draw();
rect2.updateVertices();
rect2.drawVertices();
requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>
为方便起见,我将用复数回答,但每个复数表达式都只能用实数重写。
您没有指定旋转中心(大概是原点),为了一般性,我将假设点 c
。要围绕该中心旋转任意点 p
,我们应用等式
p' = r.(p - c) + c
其中 r = cos Θ + i sin Θ
实现按角度 Θ
的旋转。
现在要旋转任何形状,假设该形状有一个参考点(比如它的中心或其他一些显着点,例如角),您需要将旋转应用于该参考点,并指定角度 {{1 }} 到形状(如果形状已经分配了角度 Θ
,则新角度为 τ
。)
如果您还想重新缩放形状,请使用 τ + Θ
,其中 r = s (cos Θ + i sin Θ)
是所需的缩放因子,并将此变换应用于参考点。还将因子 s 应用于形状的所有维度参数(或 s
是参数已经应用了因子 t . s
。)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。