2012-04-25 15 views
9

Ho una linea che si basa su due coordinate (x, y) che conosco. Questa linea ha un punto di partenza e un punto finale. Ora voglio aggiungere una punta di freccia nel punto finale della linea.Come calcolare le coordinate di una punta di freccia in base alla freccia?

So che la freccia è un triangolo equilatero e quindi ogni angolo ha 60 gradi. Inoltre, conosco la lunghezza di un lato, che sarà 20. Non ho nemmeno un lato del triangolo (cioè il punto finale della linea).

Come posso calcolare gli altri due punti del triangolo? So che dovrei usare qualche trigonometria ma come?

P.s Il punto finale della linea dovrebbe essere la punta della punta della freccia.

+1

Per chiarire: è la fine della linea nel punto centrale della base della punta della freccia, oppure è sulla punta della punta della freccia? – Chowlett

+0

È la punta della punta di freccia –

risposta

9

Non è necessario trig, solo alcuni vettore aritmetica ...

Di 'la linea va da A a B, con il vertice frontale della punta di freccia in B. La lunghezza della punta della freccia è h = 10 (√3) e la sua larghezza parziale è w = 10. Indicheremo il vettore unitario da A a B come U = (B - A)/| B - A | (vale a dire, la differenza divisa per la lunghezza della differenza) e il vettore unitario perpendicolare a questo come V = [-U y,   U x].

Da queste quantità, è possibile calcolare i due vertici posteriori della punta della freccia come B - hU ± wV.

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

Se si desidera specificare diverse angolazioni, allora si avrà bisogno di un po trig. per calcolare diversi valori di h e w. Supponendo che si desideri una punta di freccia della lunghezza h e dell'angolo di punta θ, quindi w = h   tan (θ/2). In pratica, tuttavia, è più semplice specificare direttamente h e w.

+0

Puoi fare un semplice esempio? Ho calcolato il vettore unitario, ma cosa devo fare ora? – RoflcoptrException

+0

Ottimo, funziona! – RoflcoptrException

+0

per favore puoi fare la versione javaScript? – TomP

1

È possibile trovare l'angolo di linea.

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

Quindi utilizzare questa funzione per tutti e 3 i punti utilizzando l'angolo trovato.

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

Supponendo che il punto superiore della testa della freccia è la fine della linea che sarà perfettamente ruotata e in forma di linea. Non testarlo = (

+0

"acos" restituisce solo mezzo cerchio. Hai bisogno di 'atan2'. –

3

Let la vostra linea è (x0,y0)-(x1,y1)

direzione all'indietro vettore (dx, dy) = (x0-x1, y0-y1)

norma di E Norm = Sqrt(dx*dx+dy*dy)

normalizzarlo: (udx, udy) = (dx/Norm, dy/Norm)

Ruota di angoli Pi/6 e -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 

tuoi punti: (x1 + 20 * ax, y1 + 20 * ay) e (x1 + 20 * bx, y1 + 20 * by)

8

Ecco un esempio di programma LINQPad che mostra come fare:

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

In sostanza, è:

  1. calcolare il vettore della freccia line
  2. Normalizza il vettore, es. rendendo la sua lunghezza 1
  3. Calcola le estremità delle punte di freccia andando:
    1. Prima indietro dalla testa una certa distanza
    2. Poi perpendicolare dalla linea una certa distanza

Si noti che se si desidera che le linee della freccia abbiano un'angolazione diversa da 45 gradi, sarà necessario utilizzare un metodo diverso.

Il programma sopra trarrà 10 frecce casuale ogni volta, ecco un esempio:.

arrow example

+0

Grazie, funziona, ma voglio farlo per un angolo arbitrario. – RoflcoptrException

+0

Cosa intendi per angolo arbitrario? La freccia * linea * può essere un angolo arbitrario, sembra proprio vicino a 45 nella mia immagine di esempio qui sopra, ma la testa della freccia ovviamente si gira bene con la linea della freccia, ... ma, l'angolo tra la punta della freccia e il la linea della freccia sarà di 45 gradi, proprio come nella risposta che hai accettato. –

2

Voglio contribuire con la mia risposta in C# basata sulla risposta di Marcelo Cantos poiché l'algoritmo funziona molto bene. Ho scritto un programma per calcolare il centroide di un raggio laser proiettato sull'array CCD. Dopo che il baricentro è stato trovato, viene disegnata la linea dell'angolo di direzione e ho bisogno che la freccia punta in quella direzione. Poiché l'angolo è calcolato, la testa della freccia dovrebbe seguire l'angolo in qualsiasi direzione.

Length = 10, Half-Width = 10

Length = 20, Half-Width = 10

enter image description here

Questo codice offre la flessibilità di cambiare la dimensione della testa freccia, come mostrato nelle immagini.

Innanzitutto occorre la struttura vettoriale con tutti gli operatori necessari per il sovraccarico.

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

Quindi è possibile utilizzare lo stesso codice data dal Marcelo Cantos, ma ho fatto la lunghezza e half_width delle variabili testa freccia in modo che è possibile definire che quando si chiama la funzione.

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; 

} 

Ora è possibile richiamare la funzione in questo modo:

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

Nel mio codice, il centro del cerchio è la prima posizione vettore (freccia testa), e il centroid_pixel è la seconda posizione vettore (freccia capo).

Disegno la punta della freccia memorizzando i valori vettoriali nei punti per la funzione graphics.DrawPolygon() in System.Drawings.Codice è la seguente:

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

Per chiunque sia interessato, @TomP chiedevo una versione js, ecco una versione javascript che ho fatto. Si basa sulle risposte di @Patratacus e @Marcelo Cantos. Javascript non supporta l'overloading dell'operatore, quindi non è così pulito come C++ o altri linguaggi. Sentiti libero di offrire miglioramenti.

Sto utilizzando Class.js per creare classi.

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

E poi una funzione per fare la logica:

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

e chiamare la funzione come questa:

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

So che il PO non ha chiesto di qualunque lingua specifica, ma mi sono imbattuto in questa ricerca di un'implementazione JS, quindi ho pensato di postare il risultato.

Problemi correlati