虽然这是一个简单的问题,但我可能会以错误的方式思考,因此无法找到正确的方法。
想象一下,我有一条线,比如第1行,从m_StartPoint1开始,到m_EndPoint1结束。我想绘制另一条线,比如第2行,从m_EndPoint1开始,并且与第1行具有恒定的alpha角。基本上我的目标是绘制箭头。
我使用以下代码计算第2行的x,y坐标。
const float ARROW_ANGLE=-PI/8.0;
wxPoint p;
p.x=m_EndPoint.x+ARROW_LENGTH*sin(ARROW_ANGLE);
p.y=m_EndPoint.y+ARROW_LENGTH*cos(ARROW_ANGLE);
m_ArrowHead1=new CLine(m_EndPoint,p,color,PenWidth); //Draws a line from m_EndPoint to p
当线1的角度小于90°(以度为单位)时,此计算效果很好。但是,当第1行的角度改变时,箭头没有正确显示。基本上用户应该能够按照他/她想要的方式绘制第1行,并且无论第1行的角度如何,箭头线都应该正确显示。
我已将第1行表示为向量,并通过以下代码获得了它的角度:
class CVector2D
{
wxPoint m_StartPoint, m_EndPoint;
public:
CVector2D():m_StartPoint(),m_EndPoint() {}
CVector2D(wxPoint p1, wxPoint p2):m_StartPoint(p1),m_EndPoint(p2) {}
float GetSlope(void)
{
return float(m_EndPoint.y-m_StartPoint.y)/float(m_EndPoint.x-m_StartPoint.x);
}
float GetSlopeAngleInRadians()
{
/*Will return the angle of the vector in radians
* The angle is the counterclockwise rotation therefore it is negative
*/
float slope=GetSlope();
float InRadians=atan2(float(m_EndPoint.y-m_StartPoint.y),float(m_EndPoint.x-m_StartPoint.x));
if(InRadians<=0) return InRadians;
return -(2*PI-InRadians);
}
};
然后我尝试用以下代码计算:
CVector2D vector(m_StartPoint,m_EndPoint);
float vector_angle=vector.GetSlopeAngleInRadians();
float total_angle=vector_angle+ARROW_ANGLE;
wxPoint p;
p.x=m_EndPoint.x+ARROW_LENGTH*cos(total_angle);
p.y=m_EndPoint.y+ARROW_LENGTH*sin(total_angle);
m_ArrowHead1=new CLine(m_EndPoint,p,color,PenWidth);
但是,此代码也不起作用。任何想法将不胜感激。
答案 0 :(得分:0)
这里的困难可能更多是关于数学而不是编程。但是,你需要小心处理奇点。
你如何防止在90度处被零除以:
float GetSlope(void)
{
return float(m_EndPoint.y-m_StartPoint.y)/float(m_EndPoint.x-m_StartPoint.x);
}
我推荐基于点积的数学,请参阅: How to find out if the angle between two vectors is external or internal?
PS 为什么不使用double而不是float?
答案 1 :(得分:0)
首先,要从ARROW_LENGTH
到m_EndPoint
绘制一条长度为p
的行,你应该使用它(我认为你的公式中有错误):
wxPoint p;
p.x=m_EndPoint.x+ARROW_LENGTH*cos(ARROW_ANGLE);
p.y=m_EndPoint.y+ARROW_LENGTH*sin(ARROW_ANGLE);
m_ArrowHead1=new CLine(m_EndPoint,p,color,PenWidth);
然后用相对角度绘制一条线,加上角度
new_angle = ARROW_ANGLE + ANGLE_BETWEEN_LINES
注意考虑三角函数的周期性。如果你得到一个斜坡,你说:
float GetSlope(void)
{
return float(m_EndPoint.y-m_StartPoint.y)/float(m_EndPoint.x-m_StartPoint.x);
}
请记住,这是tan(angle)
而tangent
功能不是 injection 所以这里您丢失了信息是否您的角度即在第一或第三象限。要使用描述的公式绘制线条,您必须从此(第一或第三季度参数)获得正确的角度值 - 您可以使用
std::atan2()
使用两个参数计算反正切。返回的主体值 y / x的反正切,以弧度表示。请注意,尽管如此 符号歧义,这个功能肯定地确定了哪个 象限角度下降。它需要一个小数论点。为了计算该值,该函数考虑了两个参数的符号以确定象限。
然后您可以使用此值来计算new_angle
。所以它可能看起来像这样:
float GetSlope(void)
{
return std::atan2(m_EndPoint.y-m_StartPoint.y, m_EndPoint.x-m_StartPoint.x);
}
wxPoint p;
p.x=m_EndPoint.x+ARROW_LENGTH*cos(GetSlope()+ANGLE_BETWEEN_LINES);
p.y=m_EndPoint.y+ARROW_LENGTH*sin(GetSlope()+ANGLE_BETWEEN_LINES);
m_ArrowHead1=new CLine(m_EndPoint,p,color,PenWidth);
现在,由于atan2
,您不必单独处理PI / 2和3 / 2PI案例。
答案 2 :(得分:0)
我想,你稍微复杂一点。 std::atan2
已在[-PI,PI]
范围内返回一个角度。您所要做的就是正确添加delta角度。如果我理解你的问题,你正在寻找类似的东西(code on ideone.com):
#include <iostream>
#include <cmath>
namespace so
{
struct _point_2d_
{
float x{};
float y{};
};
struct _line_2d_
{
_point_2d_ start{};
_point_2d_ end{};
};
_line_2d_ create_arrow(float _angle_delta, float _length, _line_2d_ const & _base)
{
float angle_arrow_{std::atan2(_base.end.y - _base.start.y, _base.end.x - _base.start.x) + _angle_delta};
_line_2d_ line_arrow_{};
line_arrow_.start.x = _base.end.x;
line_arrow_.start.y = _base.end.y;
line_arrow_.end.x = line_arrow_.start.x + _length * std::cos(angle_arrow_);
line_arrow_.end.y = line_arrow_.start.y + _length * std::sin(angle_arrow_);
return (line_arrow_);
}
}
int main()
{
so::_line_2d_ base_{};
float angle_delta_{0.5236f};
float length_{1.0f};
base_.start.x = 1.0f;
base_.start.y = 1.0f;
base_.end.x = -1.0f;
base_.end.y = 1.0f;
so::_line_2d_ arrow_counter{so::create_arrow(3.1416f - angle_delta_, length_, base_)};
std::cout << "(" << arrow_counter.start.x << ", " << arrow_counter.start.y << ") - (" << arrow_counter.end.x << ", "
<< arrow_counter.end.y << ")" << std::endl;
so::_line_2d_ arrow_clock{so::create_arrow(-3.1416f + angle_delta_, length_, base_)};
std::cout << "(" << arrow_clock.start.x << ", " << arrow_clock.start.y << ") - (" << arrow_clock.end.x << ", "
<< arrow_clock.end.y << ")" << std::endl;
return (0);
}
节目输出:
(-1, 1) - (-0.133971, 0.500006)
(-1, 1) - (-0.133972, 1.49999)
答案 3 :(得分:0)
我使用以下代码解决了这个问题:
CVector2D vector(m_StartPoint,m_EndPoint);
float vector_angle=vector.GetSlopeAngleInRadians();
float total_angle=vector_angle+ARROW_ANGLE; //ARROW_ANGLE=-PI/8, so is negative too
wxPoint p;
p.x=m_EndPoint.x-ARROW_LENGTH*cos(total_angle);
p.y=m_EndPoint.y-ARROW_LENGTH*sin(total_angle);
m_ArrowHead1=new CLine(m_EndPoint,p,color,PenWidth);
total_angle=vector_angle-ARROW_ANGLE;
p.x=m_EndPoint.x-ARROW_LENGTH*cos(total_angle);
p.y=m_EndPoint.y-ARROW_LENGTH*sin(total_angle);
m_ArrowHead2=new CLine(m_EndPoint,p,color,PenWidth);
无论我绘制它的方式如何,它现在都会绘制一个完美的箭头。谢谢大家的意见。