如何在C++中表示线段的向量方程?

4

我正在从事计算机图形学相关工作。

我想用两个端点表示一条线段,然后我希望我的Line2d类有一个方法返回一个Vector2d对象。

假设我有以下类:

struct Point2d
{
    int x;
    int y;
};

然后,我可以使用两个点轻松表示一条线段:

class LineSegment2d
{
private:
    Point2d start;
    Point2d end;
public:
    ...
    ...
};

根据定义,向量由大小和方向组成。
class Vector2d
{
private:
    Point2d p;
public:
    double Magnitude(void);
    Point Component(void);
    Vector2d Normal();
    Vector2d & Add(Vector & rhs);
    Vector2d & Subtract(Vector & rhs);
    Vector2d & Multiply(int scalar);
    int DotProduct(Vector2d rhs);
    Vector2d & CrossProduct(Vector2d rhs);
};
r = P + t(PQ) or r = Q + t(QP), where PQ and QP are vectors representing the direction of the line segment.
r =<1,3,2> + tv 

或者,
r =<-4,3,0> + tv

连接两点PQ的向量为,
PQ  = <(-4-1), (3-3), (0-2)>
    = <-5, 0, -2>

而且,这个向量肯定与我们的主线平行。

因此,我们可以写成:

r   =<1, 3, 2> + t <-5, 0, -2>
    =<1, 3, 2>+<-5t, 0, -2t>
    = <(1-5t), (3+0), (2-2t)>
    =<1-5t, 3, 2-2t>

根据线段的向量方程,我认为我的Vector类应该如下所示:

class LineVector2d
{
private:
    Vector2d v;
    double t;
public:
    ..........
};

这是否是正确的表示方式?

如果是的话,我该如何计算/设置/找到t的值?


1
你真正想问什么?你的问题中包含了太多问题,让我有些摸不着头脑。 - Krumelur
Line2d类的方法应该返回哪个向量? - kasterma
一个由int坐标构成的向量的大小并不总是一个int(例如,(1,1)的大小为sqrt(2))。 - kasterma
据我理解,入站或出站不是边缘的属性,而是边缘和多边形之间特定交点的属性(长边可以与多边形有入站和出站交点)。因此,这不是边缘的属性。 - kasterma
http://www.ck12.org/search/?q=domain:Directed%20Line%20Segments - user366312
6个回答

4

许多形式的线表示

如果您指的是线(而不是线段),那么可能会发现使用包含基准点和单位方向向量的类/结构很方便。

对于线段,请在(Point pt0,Point pt1)形式和(Point pt,Vector v = pt1 - pt0)形式之间选择。

第二种形式更适合参数化方法,如X = P0.X + t * D.X等。


线段是有限的,因此向量D具有确定的长度(线段的长度)。对于直线而言,存储方向向量长度没有意义,因此单位方向向量更好(用于某些计算)。 - MBo
我没有C#代码,但认为任何用C#编写的几何库都包含所需的基元。 - MBo

1
使用问题中提供的线段表示方法。
我将用LS(a,b)表示起点为a,终点为b的线段。
现在假设有两个这样的线段LS(a,b)LS(c,d)相交(在您的上下文中,一个来自剪辑多边形,另一个来自被剪辑的多边形;我假设您知道如何确定这一点)。
您似乎想要回答的是,当LS(c,d)穿过边缘LS(a,b)时,这个交点是否使LS(c,d)进入多边形内部。
为此,只需确定线段方向之间的夹角即可。这与向量v = b-aw = d-c之间的夹角相同。
此外,您甚至不需要角度,您只需要查看角度是正数还是负数; 所以请查看向量w-v。如果LS(c,d)穿过LS(a,b)到内部,则该向量将在下半平面中。如果LS(c,d)穿过LS(a,b)到外部,则该向量将在上半平面中。
确定上半平面与下半平面意味着查看end-start的第二个坐标。
对于这篇长文,我很抱歉,但是mathjax似乎在此站点上不活动。还没有代码,但是我相信(如果我没有犯任何错误),我提到的所有操作都容易转换为代码。

https://www.wolframalpha.com/input/?i=angle+between+vector+%7B0%2C180%7D+%7B180%2C0%7D - user366312
https://www.wolframalpha.com/input/?i=angle+between+vector+%7B0%2C180%7D+%7B-180%2C0%7D - user366312

1
如果您想要非常数学化,也许这可以帮助您:https://en.wikipedia.org/wiki/Homogeneous_coordinates 在二维中,位置为(x,y,1),方向为(dx,dy,0)。原因是投影,在二维中很少见但在三维中很常见。
所以尝试回答一下:始终使用4个组成部分的向量。位置具有w = 1,方向具有w = 0。
只需使用基于两点A和B的线进行尝试,两者都具有w = 1。从A到B的向量是B-A,最终结果为w = 0。
此外,您在代码中使用的内容并不重要,除非您最终优化了特殊情况。选择最小的数据结构即可。开始和结束应该没问题。
也许考虑索引:所有顶点的平面数组,每条线仅是指向顶点数组中的两个索引。

你能写一些C++代码来展示你的意思吗? - user366312

1
如果您想将线段转换为向量,您需要意识到并不存在“通用语义”来进行转换,这取决于您定义转换的含义。话虽如此,我假设您想要一个具有与线段长度相同(欧几里得)范数并指向相同方向的向量,即像这样:
class LineSegment2d
{
   ....
   Vector2d getVector() const {
      return Vector2d(end) - Vector2d(start);
   }
};

换句话说,将线段偏移至坐标系原点。然后可以将终点转换为矢量。
编辑:在了解更多关于您为什么需要这样的信息后,您可能正在寻找另一种表示方法。
class LineSegment2d
{
   ....
   Vector2d getVector() const {
      return Vector2d(end);
   }
};

这将为每条线段获取一个向量:即终点。如果您的多边形由相连的线段组成,则这将为您提供多边形中的所有顶点。

我正在使用Weiler-Atherton算法。该算法需要区分多边形边缘是入边还是出边。我不确定我的Line2d表示方式(使用startend)是否能够满足此要求。因此,我在想是否需要使用向量表示。 - user366312
嗯,你确定不想要每个多边形线段的顶点吗?(免责声明:在浏览维基百科页面一两分钟后) - Krumelur
我认为可以通过使用端点并按顺时针顺序沿线段进行实现算法。 - Krumelur

1

我认为存在以下混淆:

按照定义,向量由大小和方向组成。

有多种方法表示向量。我认为在你的问题中,你指的是一个向量可以由大小(标量)和指示方向的单位向量表示。一个向量可以只是一个有序三元组(对于三个维度),它指示了大小 (sqrt(x^2 + y^2 + z^2)) 和从原点的方向。

我认为你的问题的答案是,你不需要计算 t。请纠正我,但我认为你把 t 解释为大小?你可以用 sqrt(x^2 + y^2 + z^2)v 计算出大小,但 v 可以自身作为一个有序三元组保存大小和方向。

编辑:

template <typename T>
struct Point2d
{
    T x;
    T y;

    Point2d operator + (const Point2d<T>& rhs) const
    {
        return Point2d<T>{x + rhs.x, y + rhs.y};
    }
    Point2d operator - (const Point2d<T>& rhs) const
    {
        return Point2d<T>{x - rhs.x, y - rhs.y};
    }
    // ...

    Point2d operator * (const T value) const
    {
        return Point2d<T>{x*value, y*value};
    }
    Point2d operator / (const T value) const
    {
        return Point2d<T>{x/value, y/value};
    }
    // ...
};

template <typename T>
class Vector2d
{
private:
    Point2d<T> p;
public:
    Vector2d(const Point2d<T>& point) : p{point} {}

    /*double Magnitude();
    Point2d<T> Component();
    Vector2d Normal();
    int DotProduct(Vector2d rhs);
    Vector2d& CrossProduct(Vector2d rhs);*/

    Vector2d operator + (const Vector2d<T>& rhs) const
    {
        return p + rhs.p;
    }
    Vector2d operator - (const Vector2d<T>& rhs) const
    {
        return p - rhs.p;
    }
    // ...

    Vector2d operator * (const T value) const
    {
        return p*value;
    }
    Vector2d operator / (const T value) const
    {
        return p/value;
    }
    // ...
};

template <typename T>
class LineVector2d
{
private:
    Point2d<T>  p;
    Vector2d<T> v;

public:
    LineVector2d() = default;
    LineVector2d(const Point2d<T>& point, const Vector2d<T>& direction) : p{point}, v{direction} {}

    /// Returns the point on the line for the time/value `t`
    Point2d<T> valueAt(T t)
    {
        return p + v*t;
    }
};

@BROY,你能否澄清一下LineVector2d除了Vector2d所做的事情之外还应该做什么?我认为这会有助于回答“如何计算/设置/查找t的值?” - R2-Dequeue
好的。但是,你如何给变量t赋值?在Point2d<T> valueAt(T t)方法中,你从哪里找到/计算/确定变量t的值? - user366312
@BROY 这就是我困惑的地方,t 的取值范围从负无穷到正无穷,你可以输入任何值来获取从t=0开始的以p/point为起点的直线上的点。猜一个可能性:你是在尝试计算与某个东西的交点,并想要发生交点的t值吗? - R2-Dequeue

0

你对于类LineSegment2d的表示是正确的。但是,类Vector2d的表示是不正确的。这是因为你只考虑了那些通过原点的向量。在二维平面上,一个向量可以用三个分量表示,就像在三维空间中一样。一个向量的三个分量分别是:方向、大小和它经过的一个点。如果我们为三维空间定义x、y和z轴,那么对于x-y平面上的一个点,z分量等于0。此外,在三维空间中,方向是以方向余弦的形式定义的(即向量与轴之间的夹角的余弦)。因此,对于x-y平面上的向量,向量与z轴之间的夹角的余弦等于零(因为夹角=90度)。


请编写一个完美的Line2d类,并附上解释,以便我可以使用向量表示一条线,并且我会接受您的答案并点赞并授予100点奖励。就这些。 - user366312
你想让我写Line2d类还是Vector2d类? - Sid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接