2012-04-25 5 views
9

У меня есть строка, основанная на двух (x, y) координатах, которые я знаю. Эта строка имеет начальную и конечную точку. Теперь я хочу добавить стрелку в конечную точку строки.Как вычислить координаты стрелки на основе стрелки?

Я знаю, что стрелка является равносторонним треугольником, поэтому каждый угол имеет 60 градусов. Кроме того, я знаю длину одной стороны, которая будет 20. Я тоже не один край треугольника (то есть конечная точка линии).

Как рассчитать две другие точки треугольника? Я знаю, что я должен использовать некоторую тригонометрию, но как?

P.s. Конечной точкой линии должен быть наконечник наконечника стрелки.

+1

Чтобы уточнить - это конец линии в середине точки основания наконечника стрелки или на наконечнике наконечника стрелки? – Chowlett

+0

Это наконечник наконечника стрелки –

ответ

9

Вам не нужно триг, просто некоторый вектор арифметику ...

Say линию переходит от А к В с передней вершиной стрелки в точке В. Длина стрелки равна 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.

+0

Можете ли вы сделать простой пример? Я вычислил единичный вектор, но что мне теперь нужно делать? – RoflcoptrException

+0

Отлично, это работает! – RoflcoptrException

+0

можете ли вы сделать версию javaScript? – TomP

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

Если предположить, что верхняя точка головки стрелы является конец Лайн он будет отлично повернутый и подходит к линии. не проверял = (

+0

'acos' дает только половину круга. Вам нужно 'atan2'. –

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)

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

В принципе, вы:

  1. Вычислить вектор стрелки line
  2. Нормализовать вектор, т.е. что делает его длина 1
  3. Вычислить концы стрел на идя:
    1. Первый обратно с головы на определенное расстояние
    2. Тогда перпендикуляр из линии на определенное расстояние

Обратите внимание: если вы хотите, чтобы линии головы стрелки находились под другим углом, чем 45 градусов, вам придется использовать другой метод.

Программа выше нарисует 10 случайных стрелок каждый раз, вот пример:.

arrow example

+0

Спасибо, это работает, но я хочу сделать это под любым углом. – RoflcoptrException

+0

Что значит «произвольный угол»? Стрелка * строка * может быть произвольным углом, она просто приближается к 45 в моем примере, изображенном выше, но стрелочная головка, конечно, будет хорошо сочетаться со стрелкой, ... но угол между стрелкой и линия стрелки будет 45 градусов, точно так же, как в ответе, который вы приняли. –

2

Я хочу внести свой ответ в C# на основе ответа Марсело Кантоса, поскольку алгоритм работает очень хорошо. Я написал программу для расчета центроида лазерного луча, спроецированного на матрицу ПЗС. После того, как центроид найден, линия угла направления рисуется, и мне нужна стрелка, указывающая в этом направлении. Поскольку угол вычисляется, стрелка должна будет следовать за углом в любом направлении.

Length = 10, Half-Width = 10

Length = 20, Half-Width = 10

enter image description here

Этот код дает вам гибкость изменения размера головы стрелка, как показано на фотографии.

Сначала вам понадобится векторная структура со всеми необходимыми перегрузками операторов.

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

Затем вы можете использовать один и тот же код, заданный Марсела Песню, но я сделал длину и half_width переменной стрелки головы, так что вы можете определить, что при вызове функции.

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 является вторым вектором местоположением (стрелка глава).

Я рисую стрелку, сохраняя значения вектора в точках для функции graphics.DrawPolygon() в System.Drawings.Код показан ниже:

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

Для всех, кто заинтересован, @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]); 

Я знаю, что ОП не просил какого-либо конкретного языка, но я столкнулся с этим в поисках реализации JS, поэтому я решил опубликовать результат.

Смежные вопросы