2012-04-25 5 views
9

Ich habe eine Linie, die auf zwei (x, y) Koordinaten basiert, die ich kenne. Diese Linie hat einen Start- und einen Endpunkt. Jetzt möchte ich eine Pfeilspitze am Endpunkt der Linie hinzufügen.Wie berechnet man die Koordinaten einer Pfeilspitze basierend auf dem Pfeil?

Ich weiß, dass der Pfeil ein gleichseitiges Dreieck ist, und deshalb hat jeder Winkel 60 Grad. Außerdem kenne ich die Länge einer Seite, die 20 ist. Ich auch keine Kante des Dreiecks (das ist der Endpunkt der Linie).

Wie kann ich die anderen zwei Punkte des Dreiecks berechnen? Ich weiß, ich sollte eine Trigonometrie verwenden, aber wie?

Ps.s. Der Endpunkt der Linie sollte die Spitze der Pfeilspitze sein.

+1

Um zu klären - ist das Ende der Linie in der Mitte der Basis der Pfeilspitze, oder ist es an der Spitze der Spitze des Pfeils? – Chowlett

+0

Es ist die Spitze der Pfeilspitze –

Antwort

9

Sie brauchen nicht trig, nur einige arithmetischen Vektor ...

die Linie Say geht von A nach B, mit dem vorderen Scheitel der Pfeilspitze bei B. Die Länge der Pfeilspitze ist h = 10 (√3) und ihre halbe Breite ist w = 10. Wir bezeichnen den Einheitsvektor von A nach B als U = (B - A)/| B - A | (d. h. die Differenz dividiert durch die Länge der Differenz), und der dazu senkrechte Einheitsvektor als V = [-U ,   U x].

Aus diesen Größen können Sie die beiden hinteren Ecken der Pfeilspitze als B - hU ± wV berechnen.

In 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; 
} 

Wenn Sie verschiedene Winkel angeben wollen, dann werden Sie einige trig benötigen. um verschiedene Werte von h und w zu berechnen. Angenommen, Sie möchten eine Pfeilspitze mit der Länge h und dem Spitzenwinkel θ, dann w = h   tan (θ/2). In der Praxis ist es jedoch am einfachsten, h und w direkt anzugeben.

+0

Können Sie ein einfaches Beispiel machen? Ich habe den Einheitsvektor berechnet, aber was muss ich jetzt tun? – RoflcoptrException

+0

Großartig, das funktioniert! – RoflcoptrException

+0

bitte können Sie javaScript Version machen? – TomP

1

Sie können den Winkel der Linie finden.

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); 

Dann verwenden Sie diese Funktion zu allen 3 Punkten mit gefundenen Winkel.

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; 
} 

Unter der Annahme, dass der obere Punkt des Pfeilkopfes das Ende der Linie ist, wird es perfekt gedreht und an die Linie angepasst. Hat es nicht testen = (

+0

'acos' ergibt nur einen halben Kreis. Sie brauchen 'atan2'. –

3

Ihre Linie Lassen Sie uns ist (x0,y0)-(x1,y1)

Rückwärtsrichtungsvektor (dx, dy) = (x0-x1, y0-y1)

Es ist Norm Norm = Sqrt(dx*dx+dy*dy)

es Normalisieren: (udx, udy) = (dx/Norm, dy/Norm)

Drehen von Winkeln Pi/6 und -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 

Ihre Punkte: (x1 + 20 * ax, y1 + 20 * ay) und (x1 + 20 * bx, y1 + 20 * by)

8

Hier ist ein Beispiel LINQPad Programm, das zeigt, wie das tun:

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(); 
    } 
} 

Grundsätzlich Sie:

  1. Berechne den Vektor des Pfeiles Linie
  2. Normalisieren Sie den Vektor, dh. seine Länge 1
  3. Herstellung Berechnen das Enden der Pfeilspitzen durch gehen:
    1. Zuerst vom Kopf zurück in einem gewissen Abstand
    2. dann senkrecht aus der Linie in einem gewissen Abstand

Wenn Sie möchten, dass die Pfeilspitzen einen anderen Winkel als 45 Grad haben, müssen Sie eine andere Methode verwenden.

Das obige Programm wird 10 zufällige Pfeile jedes Mal ziehen, hier ein Beispiel:.

arrow example

+0

Danke, das funktioniert, aber ich möchte das für einen beliebigen Winkel machen. – RoflcoptrException

+0

Was meinst du mit willkürlichen Winkel? Der Pfeil * line * kann ein beliebiger Winkel sein, er sieht in meinem obigen Beispiel nur annähernd 45 aus, aber der Pfeilkopf wird sich natürlich gut mit der Pfeillinie drehen, ... aber der Winkel zwischen dem Pfeilkopf und der Pfeillinie wird 45 Grad sein, genau wie in der Antwort, die Sie angenommen haben. –

2

Ich möchte meine Antwort in C# basierend auf der Antwort von Marcelo Cantos beitragen, da der Algorithmus sehr gut funktioniert. Ich habe ein Programm geschrieben, um den Schwerpunkt eines auf das CCD-Array projizierten Laserstrahls zu berechnen. Nachdem der Schwerpunkt gefunden wurde, wird die Richtungswinkellinie gezeichnet und ich brauche den Pfeilkopf, der auf diese Richtung zeigt. Da der Winkel berechnet wird, müsste der Pfeilkopf dem Winkel in irgendeiner Richtung folgen.

Length = 10, Half-Width = 10

Length = 20, Half-Width = 10

enter image description here

Dieser Code Ihnen die Flexibilität der Änderung des Pfeilkopfgröße gibt, wie in den Abbildungen gezeigt.

Zuerst benötigen Sie die Vektorstruktur mit allen notwendigen Operatoren überladen.

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); 
    } 
} 

Dann können Sie den gleichen Code von Marcelo Cantos gegeben, aber ich machte die Länge und half_width der Pfeilkopfvariablen, so dass Sie die definieren, wann die Funktion aufrufen.

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; 

} 

Jetzt können Sie die Funktion wie folgt aufrufen:

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); 

In meinem Code ist der Kreismitte der erste Vektor Stelle (Pfeil Po) und der centroid_pixel ist der zweite Vektor Stelle (Pfeil Kopf).

Ich zeichne die Pfeilspitze durch Speichern der Vektorwerte in den Punkten für Grafiken.DrawPolygon() -Funktion in den System.Drawings.Code ist unten dargestellt:

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); 
1

Für alle, die interessiert sind, @TomP wundert sich über eine js-Version, so ist hier eine JavaScript-Version, die ich gemacht. Es basiert auf @Patratacus und @Marcelo Cantos Antworten. Javascript unterstützt nicht das Überladen von Operatoren, daher ist es nicht so sauber wie C++ oder andere Sprachen. Fühlen Sie sich frei, Verbesserungen anzubieten.

Ich benutze Class.js, um Klassen zu erstellen.

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); 
} 
}); 

Und dann eine Funktion, um die Logik zu tun:

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]; 
} 

Und die Funktion wie folgt aufrufen:

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]); 

ich die OP weiß nicht, für eine bestimmte Sprache gefragt hat, Aber ich stieß auf die Suche nach einer JS-Implementierung, also dachte ich, ich würde das Ergebnis veröffentlichen.

Verwandte Themen