1

在此处输入图像描述尽管这是一个简单的问题,但我可能以错误的方式思考,因此找不到正确的方法。

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

当 Line 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

4 回答 4

0

ARROW_LENGTH首先,要从m_EndPointto绘制一条长度线,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函数不是注入 ,所以在这里你已经丢失了你的角度是在第一象限还是第三象限的信息。要使用所描述的公式来绘制一条线,您必须从此(第一或第三季度参数)获得正确的角度值 - 您可以使用

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 案例。

于 2013-09-22T11:40:49.433 回答
0

我想,你有点过于复杂了。std::atan2已经返回范围内的角度[-PI,PI]。您所要做的就是正确添加增量角。如果我正确理解了您的问题,那么您正在寻找这样的东西(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)
于 2013-09-22T12:40:36.450 回答
0

这里的困难可能更多是关于数学而不是编程。但是,您需要小心处理奇点。

您如何防止在 90 度处被零除:

float GetSlope(void)
{
    return float(m_EndPoint.y-m_StartPoint.y)/float(m_EndPoint.x-m_StartPoint.x);
}

我推荐基于点积的数学,请参阅: 如何找出两个向量之间的角度是外部的还是内部的?

PS 为什么不使用双精度而不是浮点数?

于 2013-09-22T11:04:59.163 回答
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);

无论我以何种方式绘制它,它现在都能绘制出完美的箭头。谢谢大家的想法。

于 2013-09-25T10:44:17.553 回答