Algorithm 如何根据箭头计算箭头的坐标?
我有一条基于两个(x,y)坐标的直线。这条线有起点和终点。现在,我想在直线的端点添加一个箭头 我知道箭头是一个等边三角形,因此每个角度有60度。另外,我知道一侧的长度是20。我也没有三角形的一条边(即直线的终点) 如何计算三角形的其他两点?我知道我应该用一些三角学,但是怎么用呢Algorithm 如何根据箭头计算箭头的坐标?,algorithm,Algorithm,我有一条基于两个(x,y)坐标的直线。这条线有起点和终点。现在,我想在直线的端点添加一个箭头 我知道箭头是一个等边三角形,因此每个角度有60度。另外,我知道一侧的长度是20。我也没有三角形的一条边(即直线的终点) 如何计算三角形的其他两点?我知道我应该用一些三角学,但是怎么用呢 另外,直线的端点应该是箭头的尖端。您可以找到直线的角度 Vector ox = Vector(1,0); Vector line_direction = Vector(line_begin.x - line_end.x,
另外,直线的端点应该是箭头的尖端。您可以找到直线的角度
Vector ox = Vector(1,0);
Vector line_direction = Vector(line_begin.x - line_end.x, line_begin.y - line_end.y);
line_direction.normalize();
float angle = acos(ox.x * line_direction.x + line_direction.y * ox.y);
然后使用此功能,使用找到的角度对所有3个点进行调整
Point rotate(Point point, float angle)
{
Point rotated_point;
rotated_point.x = point.x * cos(angle) - point.y * sin(angle);
rotated_point.y = point.x * sin(angle) + point.y * cos(angle);
return rotated_point;
}
假设箭头的上端是直线的端点,它将完全旋转并适合直线。
没有测试它=(让我们看看你的线路是(x0,y0)-(x1,y1)
反向矢量(dx,dy)=(x0-x1,y0-y1)
它是normnorm=Sqrt(dx*dx+dy*dy)
规范化它:(udx,udy)=(dx/Norm,dy/Norm)
按角度旋转Pi/6
和-Pi/6
ax = udx * Sqrt(3)/2 - udy * 1/2
ay = udx * 1/2 + udy * Sqrt(3)/2
bx = udx * Sqrt(3)/2 + udy * 1/2
by = - udx * 1/2 + udy * Sqrt(3)/2
你的观点:(x1+20*ax,y1+20*ay)
和(x1+20*bx,y1+20*by)
下面是一个示例程序,演示了如何做到这一点:
void Main()
{
const int imageWidth = 512;
Bitmap b = new Bitmap(imageWidth , imageWidth , PixelFormat.Format24bppRgb);
Random r = new Random();
for (int index = 0; index < 10; index++)
{
Point fromPoint = new Point(0, 0);
Point toPoint = new Point(0, 0);
// Ensure we actually have a line
while (fromPoint == toPoint)
{
fromPoint = new Point(r.Next(imageWidth ), r.Next(imageWidth ));
toPoint = new Point(r.Next(imageWidth ), r.Next(imageWidth ));
}
// dx,dy = arrow line vector
var dx = toPoint.X - fromPoint.X;
var dy = toPoint.Y - fromPoint.Y;
// normalize
var length = Math.Sqrt(dx * dx + dy * dy);
var unitDx = dx / length;
var unitDy = dy / length;
// increase this to get a larger arrow head
const int arrowHeadBoxSize = 10;
var arrowPoint1 = new Point(
Convert.ToInt32(toPoint.X - unitDx * arrowHeadBoxSize - unitDy * arrowHeadBoxSize),
Convert.ToInt32(toPoint.Y - unitDy * arrowHeadBoxSize + unitDx * arrowHeadBoxSize));
var arrowPoint2 = new Point(
Convert.ToInt32(toPoint.X - unitDx * arrowHeadBoxSize + unitDy * arrowHeadBoxSize),
Convert.ToInt32(toPoint.Y - unitDy * arrowHeadBoxSize - unitDx * arrowHeadBoxSize));
using (Graphics g = Graphics.FromImage(b))
{
if (index == 0)
g.Clear(Color.White);
g.DrawLine(Pens.Black, fromPoint, toPoint);
g.DrawLine(Pens.Black, toPoint, arrowPoint1);
g.DrawLine(Pens.Black, toPoint, arrowPoint2);
}
}
using (var stream = new MemoryStream())
{
b.Save(stream, ImageFormat.Png);
Util.Image(stream.ToArray()).Dump();
}
}
void Main()
{
常量int imageWidth=512;
位图b=新位图(imageWidth、imageWidth、PixelFormat.Format24bppRgb);
随机r=新随机();
对于(int-index=0;index<10;index++)
{
点fromPoint=新点(0,0);
点拓扑点=新点(0,0);
//确保我们有一条线路
while(fromPoint==toPoint)
{
fromPoint=新点(r.Next(图像宽度),r.Next(图像宽度));
toPoint=新点(r.Next(图像宽度),r.Next(图像宽度));
}
//dx,dy=箭头线矢量
var dx=toPoint.X-fromPoint.X;
var dy=toPoint.Y—fromPoint.Y;
//正常化
变量长度=数学Sqrt(dx*dx+dy*dy);
var unitDx=dx/长度;
var unitDy=dy/长度;
//增加此值可获得更大的箭头
常数int arrowHeadBoxSize=10;
var ARROWSPOINT1=新点(
将.ToInt32(拓扑点X-unitDx*箭头头框大小-unitDy*箭头头框大小),
将.ToInt32(拓扑点Y-unitDy*箭头箱尺寸+unitDx*箭头箱尺寸));
var ARROWSPOINT2=新点(
将.ToInt32(拓扑点X-unitDx*箭头头框大小+unitDy*箭头头框大小),
将.ToInt32(拓扑点Y-unitDy*箭头头框大小-unitDx*箭头头框大小));
使用(Graphics g=Graphics.FromImage(b))
{
如果(索引==0)
g、 清晰(颜色:白色);
g、 抽绳(黑色、起点、终点);
g、 抽绳(钢笔、黑色、拓朴点、箭头点1);
g、 抽绳(钢笔、黑色、圆点、箭头点2);
}
}
使用(var stream=new MemoryStream())
{
b、 保存(流,ImageFormat.Png);
Util.Image(stream.ToArray()).Dump();
}
}
基本上,你:
你不需要三角,只需要一些向量运算 假设直线从A到B,箭头的前顶点位于B。箭头的长度为h=10(√3) 它的半宽度是w=10。我们将把从A到B的单位向量表示为U=(B-A)/| B-A |(即差值除以差值的长度),垂直于此的单位向量表示为V=[-Uy,Ux] 根据这些数量,可以将箭头的两个后顶点计算为B-hU±wV 在C++中:
struct vec { float x, y; /* … */ };
void arrowhead(vec A, vec B, vec& v1, vec& v2) {
float h = 10*sqrtf(3), w = 10;
vec U = (B - A)/(B - A).length();
vec V = vec(-U.y, U.x);
v1 = B - h*U + w*V;
v2 = B - h*U - w*V;
}
如果要指定不同的角度,则需要一些触发器来计算
h
和w
的不同值。假设需要长度为h且尖端角度为θ的箭头,则w=htan(θ/2).然而,在实践中,直接指定h
和w
是最简单的。我想根据Marcelo Cantos的答案给出我的答案,因为该算法非常有效。我编写了一个程序来计算投射到CCD阵列上的激光束的质心。找到质心后,方向角为e被画出来,我需要箭头指向那个方向。因为角度是经过计算的,所以箭头必须沿着任何方向的角度
此代码为您提供了更改箭头大小的灵活性,如图所示
首先,需要向量结构和所有必需的运算符重载
private struct vec
{
public float x;
public float y;
public vec(float x, float y)
{
this.x = x;
this.y = y;
}
public static vec operator -(vec v1, vec v2)
{
return new vec(v1.x - v2.x, v1.y - v2.y);
}
public static vec operator +(vec v1, vec v2)
{
return new vec(v1.x + v2.x, v1.y + v2.y);
}
public static vec operator /(vec v1, float number)
{
return new vec(v1.x / number, v1.y / number);
}
public static vec operator *(vec v1, float number)
{
return new vec(v1.x * number, v1.y * number);
}
public static vec operator *(float number, vec v1)
{
return new vec(v1.x * number, v1.y * number);
}
public float length()
{
double distance;
distance = (this.x * this.x) + (this.y * this.y);
return (float)Math.Sqrt(distance);
}
}
然后,您可以使用Marcelo Cantos给出的相同代码,但我对箭头变量的长度和半宽度进行了调整,以便您在调用函数时可以对其进行定义
private void arrowhead(float length, float half_width,
vec A, vec B, ref vec v1, ref vec v2)
{
float h = length * (float)Math.Sqrt(3);
float w = half_width;
vec U = (B - A) / (B - A).length();
vec V = new vec(-U.y, U.x);
v1 = B - h * U + w * V;
v2 = B - h * U - w * V;
}
现在您可以这样调用函数:
vec leftArrowHead = new vec();
vec rightArrowHead = new vec();
arrowhead(20, 10, new vec(circle_center_x, circle_center_y),
new vec(x_centroid_pixel, y_centroid_pixel),
ref leftArrowHead, ref rightArrowHead);
var A = new Vector(start.x,start.y);
var B = new Vector(end.x,end.y);
var vec = getArrowhead(A,B);
console.log(vec[0]);
console.log(vec[1]);
在我的代码中,圆心是第一个矢量位置(箭头对接),质心_像素是第二个矢量位置(箭头)
我通过在System.Drawings中的points for graphics.DrawPolygon()函数中存储矢量值来绘制箭头。代码如下所示:
Point[] ppts = new Point[3];
ppts[0] = new Point((int)leftArrowHead.x, (int)leftArrowHead.y);
ppts[1] = new Point(x_cm_pixel,y_cm_pixel);
ppts[2] = new Point((int)rightArrowHead.x, (int)rightArrowHead.y);
g2.DrawPolygon(p, ppts);
对于任何感兴趣的人,TomP都在想一个JS版本,所以这里是我制作的一个JavaScript版本。它是基于@ PATRATACUS和马塞洛CANTOS的答案。JavaScript不支持操作符重载,所以它不像C++或其他语言那样干净。
var getArrowhead = function(A, B)
{
var h = 10 * Math.sqrt(3);
var w = 5;
var v1 = B.subtract(A);
var length = v1.length();
var U = v1.divide(length);
var V = new Vector(-U.y, U.x);
var r1 = B.subtract(U.multiply(h)).add(V.multiply(w));
var r2 = B.subtract(U.multiply(h)).subtract(V.multiply(w));
return [r1,r2];
}
var A = new Vector(start.x,start.y);
var B = new Vector(end.x,end.y);
var vec = getArrowhead(A,B);
console.log(vec[0]);
console.log(vec[1]);