2012-04-25 37 views
9

Tengo una línea que se basa en dos (x, y) coordenadas que conozco. Esta línea tiene un punto inicial y un punto final. Ahora quiero agregar una punta de flecha en el punto final de la línea.¿Cómo calcular las coordenadas de una punta de flecha basada en la flecha?

Sé que la flecha es un triángulo equilátero, y por lo tanto cada ángulo tiene 60 grados. Además, sé la longitud de un lado, que será 20. Tampoco tengo un borde del triángulo (ese es el punto final de la línea).

¿Cómo puedo calcular los otros dos puntos del triángulo? Sé que debería usar algo de trigonometría, pero ¿cómo?

P.s. El punto final de la línea debe ser la punta de la punta de flecha.

+1

Para aclarar: ¿está el final de la línea en el punto medio de la base de la punta de la flecha, o está en la punta de la punta de la flecha? – Chowlett

+0

Es la punta de la punta de flecha –

Respuesta

9

No es necesario trig, sólo algunas vector de la aritmética ...

Diga la línea va de A a B, con el vértice delantero de la punta de flecha en B. La longitud de la punta de flecha es h = 10 (√3) y su ancho medio es w = 10. Denotaremos el vector unitario de A a B como U = (B - A)/| B - A | (es decir, la diferencia dividida por la longitud de la diferencia) y el vector unitario perpendicular a esto como V = [-U y,   U x].

A partir de estas cantidades, puede calcular los dos vértices traseros de la punta de flecha como B - hU ± wV.

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

Si desea especificar diferentes ángulos, a continuación, usted necesitará un poco de trigonometría. para calcular diferentes valores de h y w. Suponiendo que quiere una punta de flecha de longitud h y ángulo de punta θ, entonces w = h   tan (θ/2). En la práctica, sin embargo, es más simple especificar h y w directamente.

+0

¿Puedes hacer un ejemplo simple? Calculé el vector unitario, pero ¿qué tengo que hacer ahora? – RoflcoptrException

+0

¡Genial, eso funciona! – RoflcoptrException

+0

por favor ¿puedes hacer la versión de javaScript? – TomP

1

Puede encontrar el ángulo de línea.

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

Luego use esta función para los 3 puntos utilizando el ángulo encontrado.

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

Suponiendo que el punto superior de la cabeza de flecha es el final de la línea que lo hará perfectamente girada y en forma de línea. No probarlo = (

+0

'acos' solo produce medio círculo. Necesitas 'atan2'. –

3

Vamos a su línea está (x0,y0)-(x1,y1)

hacia atrás vector de dirección (dx, dy) = (x0-x1, y0-y1)

norma de lo Norm = Sqrt(dx*dx+dy*dy)

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

Girar ángulos Pi/6 y -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 

Sus puntos: (x1 + 20 * ax, y1 + 20 * ay) y (x1 + 20 * bx, y1 + 20 * by)

8

Aquí está un ejemplo de programa LINQPad que muestra cómo hacerlo:

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

Básicamente, usted:

  1. calcular el vector de la flecha línea
  2. Normalice el vector, es decir. haciendo de su longitud 1
  3. Calcula los extremos de las puntas de flecha por ir:
    1. primer lugar detrás de la cabeza de una cierta distancia
    2. Entonces perpendicular hacia fuera de la línea de una cierta distancia

Tenga en cuenta que si desea que las líneas de las flechas tengan un ángulo diferente de 45 grados, tendrá que usar un método diferente.

El programa anterior dibujará 10 flechas al azar cada vez, he aquí un ejemplo:.

arrow example

+0

Gracias, esto funciona, pero quiero hacer eso para un ángulo arbitrario. – RoflcoptrException

+0

¿Qué quiere decir ángulo arbitrario? La flecha * línea * puede ser un ángulo arbitrario, se ve cerca de 45 en mi imagen de ejemplo anterior, pero la punta de la flecha, por supuesto, girará muy bien con la línea de flecha, pero el ángulo entre la punta de la flecha y el la línea de la flecha será de 45 grados, al igual que en la respuesta que usted aceptó. –

2

Quiero contribuir con mi respuesta en C# según la respuesta de Marcelo Cantos, ya que el algoritmo funciona muy bien. Escribí un programa para calcular el centroide de un rayo láser proyectado en la matriz CCD. Después de encontrar el centroide, se dibuja la línea del ángulo de dirección y necesito la punta de la flecha apuntando en esa dirección. Como se calcula el ángulo, la punta de la flecha debería seguir el ángulo en cualquiera de las direcciones.

Length = 10, Half-Width = 10

Length = 20, Half-Width = 10

enter image description here

Este código le da la flexibilidad de cambiar el tamaño de la punta de la flecha, como se muestra en las fotos.

Primero necesita la estructura vectorial con todas las sobrecargas de operadores necesarias.

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

A continuación, puede utilizar el mismo código dado por Marcelo Cantos, pero hice lo largo y half_width de las variables punta de flecha de manera que se puede definir que cuando se llama a la función.

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; 

} 

Ahora se puede llamar a la función como esta:

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

En mi código, el centro del círculo es la primera ubicación de vectores (flecha a tope), y la centroid_pixel es la segunda ubicación del vector (flecha cabeza).

Dibujo la punta de flecha almacenando los valores vectoriales en los puntos para la función graphics.DrawPolygon() en System.Drawings.El código se muestra a continuación:

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

Para cualquier persona que esté interesada, @TomP preguntaba acerca de una versión JS, asi que aquí hay una versión javascript que hice. Se basa en respuestas de @Patratacus y @Marcelo Cantos. Javascript no es compatible con la sobrecarga del operador, por lo que no es tan limpio como C++ u otros idiomas. Siéntase libre de ofrecer mejoras.

Estoy usando Class.js para crear clases.

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

Y entonces una función para hacer la lógica:

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

y llamar a la función como esta:

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

Sé el PO no pidió ningún idioma específico, pero me encontré con esto en busca de una implementación de JS, así que pensé en publicar el resultado.