2012-04-25 44 views
9

Tôi có một dòng dựa trên hai (x, y) tọa độ mà tôi biết. Dòng này có điểm bắt đầu và điểm kết thúc. Bây giờ tôi muốn thêm một đầu mũi tên ở điểm cuối của dòng.Cách tính tọa độ của đầu mũi tên dựa trên mũi tên?

Tôi biết rằng mũi tên là hình tam giác đều, và do đó mỗi góc có 60 độ. Ngoài ra, tôi biết chiều dài của một bên, mà sẽ là 20. Tôi cũng không có một cạnh của tam giác (đó là điểm cuối của dòng).

Làm cách nào để tính hai điểm khác của tam giác? Tôi biết tôi nên sử dụng một lượng giác nhưng làm thế nào?

P.s. Điểm cuối của đường thẳng sẽ là đầu mũi tên.

+1

Để làm rõ - là kết thúc của dòng ở giữa điểm gốc của đầu mũi tên hay là đầu mũi tên của đầu mũi tên? – Chowlett

+0

Đó là mũi tên của đầu mũi tên –

Trả lời

9

Bạn không cần phải trang điểm, chỉ cần một số số học vector ...

Say dòng đi từ A đến B, với đỉnh phía trước của đầu mũi tên tại B. Chiều dài của đầu mũi tên là h = 10 (√3) và nửa chiều rộng của nó là w = 10. Chúng ta sẽ biểu thị vectơ đơn vị từ A đến B như U = (B - A)/| B - A | (tức là sự khác biệt chia cho độ dài của sự khác biệt) và vectơ đơn vị vuông góc với điều này là V = [-U y,   U x].

Từ những đại lượng này, bạn có thể tính hai đỉnh sau của đầu mũi tên là B - hU ± wV.

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

Nếu bạn muốn xác định góc độ khác nhau, sau đó bạn sẽ cần một số lượng giác. để tính các giá trị khác nhau của hw. Giả sử bạn muốn đầu mũi tên có chiều dài h và góc nhọn θ, thì w = h   tan (θ/2). Tuy nhiên, trong thực tế, cách đơn giản nhất là chỉ định trực tiếp hw.

+0

Bạn có thể tạo một ví dụ đơn giản không? Tôi đã tính toán vector đơn vị, nhưng tôi phải làm gì bây giờ? – RoflcoptrException

+0

Tuyệt vời, hoạt động! – RoflcoptrException

+0

bạn có thể tạo phiên bản javaScript không? – TomP

1

Bạn có thể tìm góc của đường kẻ.

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

Sau đó, sử dụng chức năng này cho cả 3 điểm sử dụng góc tìm thấy.

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

Giả sử điểm trên đầu mũi tên là đầu cuối của đường thẳng sẽ xoay hoàn toàn và vừa với đường thẳng. Không kiểm tra nó = (

+0

'acos' chỉ mang lại một nửa vòng tròn. Bạn cần 'atan2'. –

3

Hãy dòng của bạn là (x0,y0)-(x1,y1)

vector hướng ngược (dx, dy) = (x0-x1, y0-y1)

Đó là chuẩn mực Norm = Sqrt(dx*dx+dy*dy)

Đồng hóa nó: (udx, udy) = (dx/Norm, dy/Norm)

Xoay bởi góc 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 

điểm của bạn: (x1 + 20 * ax, y1 + 20 * ay)(x1 + 20 * bx, y1 + 20 * by)

8

Dưới đây là một ví dụ chương trình LINQPad cho thấy làm thế nào để làm điều đó:

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

Về cơ bản, bạn:

  1. Tính vector của mũi tên dòng
  2. Bình thường hóa vectơ, ví dụ: làm cho chiều dài của nó 1
  3. Tính mục đích của người đứng đầu mũi tên bằng cách:
    1. Đầu tiên trở lại từ đầu một khoảng cách nhất định
    2. Sau đó vuông góc ra từ dòng một khoảng cách nhất định

Lưu ý rằng nếu bạn muốn các đường đầu mũi tên có góc khác hơn 45 độ, bạn sẽ phải sử dụng một phương pháp khác.

Chương trình trên sẽ thu hút 10 mũi tên ngẫu nhiên mỗi lần, sau đây là một ví dụ:.

arrow example

+0

Cảm ơn, công trình này, nhưng tôi muốn làm điều đó cho một góc tùy ý. – RoflcoptrException

+0

Bạn có ý nghĩa gì với góc tùy ý? Mũi tên * dòng * có thể là một góc tùy ý, nó chỉ trông gần 45 trong hình ảnh ví dụ của tôi ở trên, nhưng đầu mũi tên dĩ nhiên sẽ trở nên độc đáo với đường mũi tên, ... nhưng, góc giữa đầu mũi tên và đường mũi tên sẽ là 45 độ, giống như trong câu trả lời bạn đã chấp nhận. –

2

Tôi muốn đóng góp câu trả lời của mình trong C# dựa trên câu trả lời của Marcelo Cantos vì thuật toán hoạt động thực sự tốt. Tôi đã viết một chương trình để tính toán trọng tâm của chùm tia laser chiếu trên mảng CCD. Sau khi tìm thấy trọng tâm, đường góc hướng được vẽ và tôi cần đầu mũi tên chỉ hướng đó. Vì góc được tính toán, đầu mũi tên sẽ phải theo góc theo bất kỳ hướng nào.

Length = 10, Half-Width = 10

Length = 20, Half-Width = 10

enter image description here

Mã này mang đến cho bạn sự linh hoạt của việc thay đổi kích thước mũi tên đầu như thể hiện trong hình ảnh.

Trước tiên, bạn cần cấu trúc vectơ với tất cả các toán tử cần thiết quá tải.

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

Sau đó, bạn có thể sử dụng cùng mã do Marcelo Cantos đưa ra, nhưng tôi đã thực hiện độ dài và half_width của các biến đầu mũi tên để bạn có thể xác định khi gọi hàm.

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; 

} 

Bây giờ bạn có thể gọi hàm như thế này:

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

Trong mã của tôi, trung tâm vòng tròn là vị trí vector đầu tiên (mũi tên mông), và centroid_pixel là vị trí vector thứ hai (mũi tên cái đầu).

Tôi vẽ đầu mũi tên bằng cách lưu các giá trị vectơ vào các điểm cho đồ họa.DrawPolygon() trong System.Drawings.Mã được hiển thị bên dưới:

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

Đối với bất kỳ ai quan tâm, @TomP đã tự hỏi về phiên bản js, vì vậy, đây là phiên bản javascript mà tôi đã tạo. Nó dựa trên @Patratacus và @Marcelo Cantos câu trả lời. Javascript không hỗ trợ quá tải toán tử, vì vậy nó không phải là sạch sẽ như C++ hoặc các ngôn ngữ khác. Vui lòng cung cấp các cải tiến.

Tôi đang sử dụng Class.js để tạo lớp học.

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

Và sau đó một chức năng để làm logic:

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

Và gọi hàm như thế này:

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

Tôi biết OP đã không yêu cầu bất kỳ ngôn ngữ cụ thể, nhưng tôi đã xem xét điều này để tìm kiếm một triển khai JS, vì vậy tôi nghĩ rằng tôi sẽ đăng kết quả.