10

私が知っている 2 つの (x,y) 座標に基づく線があります。この線には始点と終点があります。次に、線の終点に矢印を追加します。

矢印が正三角形であることはわかっているので、それぞれの角度は 60 度です。さらに、20 になる 1 辺の長さを知っています。また、三角形の 1 つのエッジ (つまり、線の終点) も知りません。

三角形の他の 2 点を計算するにはどうすればよいですか? 三角法を使用する必要があることはわかっていますが、どうすればよいですか?

Ps 線の終点は、矢印の先端である必要があります。

4

6 に答える 6

10

これを行う方法を示すサンプル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();
    }
}

基本的に、あなたは:

  1. 矢線のベクトルを計算する
  2. ベクトルを正規化します。その長さを 1 にする
  3. 次のようにして、矢印の先端を計算します。
    1. 最初に頭から一定距離後退
    2. 次に、線から一定の距離だけ垂直に外に出ます

矢じりの線を 45 度以外の角度にしたい場合は、別の方法を使用する必要があることに注意してください。

上記のプログラムは、毎回 10 個のランダムな矢印を描画します。以下に例を示します。

矢印の例

于 2012-04-25T13:21:09.340 に答える
10

三角関数は必要ありません。ベクトル演算だけです...

線が A から B に向かい、矢印の前の頂点が B であるとします。矢印の長さは h = 10(√3) で、その半幅は w = 10 です。 A から B として U = (B - A)/|B - A| (つまり、差を差の長さで割った値)、およびこれに垂直な単位ベクトル V = [-U y , U x ]。

これらの量から、矢じりの後ろの 2 つの頂点を 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

于 2012-04-25T13:22:00.357 に答える
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)

于 2012-04-25T13:12:38.797 に答える
2

アルゴリズムが非常にうまく機能するため、Marcelo Cantos の回答に基づいて C# で回答を提供したいと思います。CCDアレイに投影されたレーザービームの重心を計算するプログラムを書きました。重心が見つかった後、方向角度線が描かれ、その方向を指す矢印の頭が必要です。角度が計算されるため、矢印の先端はいずれかの方向の角度に従う必要があります。

長さ = 10、半幅 = 10

長さ = 20、半幅 = 10

ここに画像の説明を入力

このコードを使用すると、図に示すように矢印のサイズを柔軟に変更できます。

まず、必要なすべての演算子をオーバーロードした vector 構造体が必要です。

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 が 2 番目のベクトル位置 (矢頭) です。

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);
于 2012-12-21T19:54:52.877 に答える
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の実装を探しているときにこれに出くわしたので、結果を投稿すると思いました.

于 2016-09-28T15:16:02.233 に答える
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;
}

矢印の頭の上端が線の終点であると仮定すると、矢印は完全に回転して線にフィットします。テストしませんでした =(

于 2012-04-25T13:10:08.953 に答える