我有一条基于我知道的两个(x,y)坐标的线。该行有一个起点和终点。现在我想在该行的终点添加一个箭头。
我知道箭头是等边三角形,因此每个角度都有60度。另外,我知道一边的长度,即20。我也没有三角形的边缘(这是直线的终点)。
如何计算三角形的其他两个点?我知道我应该使用一些三角函数,但是如何?
P.S。该线的终点应该是箭头的尖端。
答案 0 :(得分:9)
你不需要触发器,只需要一些矢量算术......
假设线从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
。
答案 1 :(得分:8)
这是一个示例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个随机箭头,这里有一个例子:
答案 2 :(得分:3)
我们的行是(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)
答案 3 :(得分:2)
我想根据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);
答案 4 :(得分:1)
你可以找到线的角度。
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;
}
假设箭头的上部是线的末端,它将完美地旋转并适合线。 没试过它=(
答案 5 :(得分:1)
对于任何有兴趣的人,@ 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实现,所以我想我会发布结果。