gợi ý đầu tiên về phương pháp của bạn:
Những gì bạn gọi ac
thực sự là cb
. Nhưng đó là ok, đây là những gì thực sự cần thiết. Tiếp theo,
float dotabac = (ab.x * ab.y + ac.x * ac.y);
Đây là sai lầm đầu tiên của bạn. Các thực chấm sản phẩm của hai vectơ là:
float dotabac = (ab.x * ac.x + ab.y * ac.y);
Bây giờ,
float rslt = acos(dacos);
Ở đây bạn nên lưu ý rằng do một số tổn thất chính xác trong việc tính toán đó là về mặt lý thuyết có thể là dacos
sẽ trở nên lớn hơn 1 (hoặc nhỏ hơn -1). Do đó - bạn nên kiểm tra điều này một cách rõ ràng.
Thêm ghi chú hiệu suất: bạn gọi hàm nặng sqrt
hai lần để tính chiều dài của hai vectơ. Sau đó, bạn chia sản phẩm dấu chấm theo các độ dài đó. Thay vào đó, bạn có thể gọi sqrt
trên phép nhân của bình phương chiều dài của cả hai vectơ.
Và cuối cùng, bạn nên lưu ý rằng kết quả của bạn chính xác đến số sign
. Nghĩa là, phương pháp của bạn sẽ không phân biệt được 20 ° và -20 °, vì cosin của cả hai đều giống nhau. Phương pháp của bạn sẽ mang lại góc tương tự cho ABC và CBA.
Một phương pháp chính xác cho việc tính toán góc là như "oslvbo" gợi ý:
float angba = atan2(ab.y, ab.x);
float angbc = atan2(cb.y, cb.x);
float rslt = angba - angbc;
float rs = (rslt * 180)/3.141592;
(Tôi vừa mới thay thế atan
bởi atan2
).
Đó là phương pháp đơn giản nhất, luôn mang lại kết quả chính xác. Hạn chế của phương pháp này là bạn thực sự gọi hàm lượng giác nặng atan2
hai lần.
Tôi đề xuất phương pháp sau. Đó là một chút phức tạp hơn (đòi hỏi một số kỹ năng lượng giác để hiểu), tuy nhiên nó vượt trội so với quan điểm thực hiện. Nó chỉ gọi một lần hàm lượng giác atan2
. Và không có tính toán căn bậc hai.
int CGlEngineFunctions::GetAngleABC(POINTFLOAT a, POINTFLOAT b, POINTFLOAT c)
{
POINTFLOAT ab = { b.x - a.x, b.y - a.y };
POINTFLOAT cb = { b.x - c.x, b.y - c.y };
// dot product
float dot = (ab.x * cb.x + ab.y * cb.y);
// length square of both vectors
float abSqr = ab.x * ab.x + ab.y * ab.y;
float cbSqr = cb.x * cb.x + cb.y * cb.y;
// square of cosine of the needed angle
float cosSqr = dot * dot/abSqr/cbSqr;
// this is a known trigonometric equality:
// cos(alpha * 2) = [ cos(alpha) ]^2 * 2 - 1
float cos2 = 2 * cosSqr - 1;
// Here's the only invocation of the heavy function.
// It's a good idea to check explicitly if cos2 is within [-1 .. 1] range
const float pi = 3.141592f;
float alpha2 =
(cos2 <= -1) ? pi :
(cos2 >= 1) ? 0 :
acosf(cos2);
float rslt = alpha2/2;
float rs = rslt * 180./pi;
// Now revolve the ambiguities.
// 1. If dot product of two vectors is negative - the angle is definitely
// above 90 degrees. Still we have no information regarding the sign of the angle.
// NOTE: This ambiguity is the consequence of our method: calculating the cosine
// of the double angle. This allows us to get rid of calling sqrt.
if (dot < 0)
rs = 180 - rs;
// 2. Determine the sign. For this we'll use the Determinant of two vectors.
float det = (ab.x * cb.y - ab.y * cb.y);
if (det < 0)
rs = -rs;
return (int) floor(rs + 0.5);
}
EDIT:
Gần đây tôi đã làm việc trên một chủ đề có liên quan. Và sau đó tôi đã nhận ra có một cách tốt hơn. Nó thực sự nhiều hơn hoặc ít hơn (phía sau hậu trường). Tuy nhiên nó là IMHO đơn giản hơn.
Ý tưởng là xoay cả hai vectơ sao cho giá trị đầu tiên được căn chỉnh theo hướng X dương (dương). Rõ ràng xoay cả hai vectơ không ảnh hưởng đến góc giữa chúng. OTOH sau một vòng quay như vậy chỉ cần tìm ra góc của vector thứ 2 liên quan đến trục X. Và đây chính là điều mà atan2
dành cho.
Rotation được thực hiện bằng cách nhân một vector của các ma trận sau:
Khi có thể thấy rằng vector a
nhân với một ma trận thực sự như vậy quay về phía trục X dương.
Lưu ý: Nói đúng ma trận ở trên không chỉ xoay, nó còn mở rộng quy mô. Nhưng điều này là ok trong trường hợp của chúng tôi, vì điều duy nhất quan trọng là hướng vector, không phải chiều dài của nó.
Đã xoay vector b
trở thành:
- a.x * b.x + a.y * b.y = một dot b
- -a.y * b.x + a.x * b.y = một chéo b
Cuối cùng, câu trả lời có thể được diễn tả như:
int CGlEngineFunctions::GetAngleABC(POINTFLOAT a, POINTFLOAT b, POINTFLOAT c)
{
POINTFLOAT ab = { b.x - a.x, b.y - a.y };
POINTFLOAT cb = { b.x - c.x, b.y - c.y };
float dot = (ab.x * cb.x + ab.y * cb.y); // dot product
float cross = (ab.x * cb.y - ab.y * cb.x); // cross product
float alpha = atan2(cross, dot);
return (int) floor(alpha * 180./pi + 0.5);
}
tôi đang làm khá tốt, tôi có một algorthm cho nó nhưng nó đã không làm các trick. – jmasterx
@abelenky: và điều đó làm cho câu hỏi "không rõ ràng hoặc không hữu ích", chính xác như thế nào? Bạn có thể đã hiểu lầm mục đích của đại diện. Nó không ở đó để cho phép bạn trừng phạt mọi người vì cố gắng làm điều gì đó mới mẻ đối với họ. – jalf