我有一条基于我知道的两个 (x,y) 坐标的线。这条线有起点和终点。现在我想在该行的终点添加一个箭头。
我知道箭头是等边三角形,因此每个角度都有 60 度。另外,我知道一侧的长度,即 20。我也没有三角形的一条边(即线的终点)。
如何计算三角形的其他两个点?我知道我应该使用一些三角函数但是如何?
Ps 线的端点应该是箭头的尖端。
我有一条基于我知道的两个 (x,y) 坐标的线。这条线有起点和终点。现在我想在该行的终点添加一个箭头。
我知道箭头是等边三角形,因此每个角度都有 60 度。另外,我知道一侧的长度,即 20。我也没有三角形的一条边(即线的终点)。
如何计算三角形的其他两个点?我知道我应该使用一些三角函数但是如何?
Ps 线的端点应该是箭头的尖端。
这是一个示例LINQPad程序,它展示了如何做到这一点:
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();
}
}
基本上,你:
请注意,如果您希望箭头线的角度不同于 45 度,则必须使用不同的方法。
上面的程序每次会随机绘制 10 个箭头,下面是一个例子:
你不需要三角函数,只需要一些向量算术......
假设这条线从 A 到 B,箭头的前顶点在 B。箭头的长度是 h = 10(√3),它的半宽是 w = 10。我们将表示单位向量A 到 B 为 U = (B - A)/|B - A| (即,差除以差的长度),与此垂直的单位向量为 V = [-U y , U x ]。
根据这些量,您可以将箭头的两个后顶点计算为 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 = h tan(θ/2)。然而,在实践中,直接指定是最简单h
的w
。
让你的线是(x0,y0)-(x1,y1)
后向矢量(dx, dy) = (x0-x1, y0-y1)
这是常态Norm = 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)
我想根据 Marcelo Cantos 的回答在 C# 中贡献我的答案,因为该算法运行良好。我编写了一个程序来计算投射在 CCD 阵列上的激光束的质心。找到质心后,绘制方向角线,我需要指向该方向的箭头。由于计算了角度,因此箭头必须在任何方向上跟随角度。
此代码使您可以灵活地更改箭头大小,如图所示。
首先,您需要具有所有必要运算符重载的向量结构。
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);
在我的代码中,圆心是第一个矢量位置(箭头对接),centroid_pixel 是第二个矢量位置(箭头)。
我通过将向量值存储在 System.Drawings 中 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);
你可以找到线的角度。
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;
}
假设箭头头的上点是线的末端,它将完美地旋转并适合线。没测试=(
对于任何感兴趣的人,@TomP 想知道一个 js 版本,所以这是我制作的一个 javascript 版本。它基于@Patratacus 和@Marcelo Cantos 的答案。Javascript 不支持运算符重载,因此它不像 C++ 或其他语言那样干净。随时提供改进。
我正在使用 Class.js 创建类。
Vector = Class.extend({
NAME: "Vector",
init: function(x, y)
{
this.x = x;
this.y = y;
},
subtract: function(v1)
{
return new Vector(this.x - v1.x, this.y - v1.y);
},
add: function(v1)
{
return new Vector(this.x + v1.x, this.y + v1.y);
},
divide: function(number)
{
return new Vector(this.x / number, this.y / number);
},
multiply: function(number)
{
return new Vector(this.x * number, this.y * number);
},
length: function()
{
var distance;
distance = (this.x * this.x) + (this.y * this.y);
return Math.sqrt(distance);
}
});
然后是一个执行逻辑的函数:
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]);
我知道 OP 没有要求任何特定的语言,但我在寻找 JS 实现时遇到了这个问题,所以我想我会发布结果。