相对于另一条线的线的角度

时间:2013-09-22 10:38:07

标签: c++ user-interface geometry

enter image description here虽然这是一个简单的问题,但我可能会以错误的方式思考,因此无法找到正确的方法。

想象一下,我有一条线,比如第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);

但是,此代码也不起作用。任何想法将不胜感激。

4 个答案:

答案 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_LENGTHm_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);

无论我绘制它的方式如何,它现在都会绘制一个完美的箭头。谢谢大家的意见。