天天看點

矢量線的一種栅格化算法

總結了一種矢量線的栅格化算法。

目錄

  • 1. 概述
    • 1.1. 已知算法
    • 1.2. 本文算法
  • 2. 實作
  • 3. 參考

将一條線段栅格化的最簡單的算法思路是根據其斜率,按X或Y方向步進取值:

矢量線的一種栅格化算法
矢量線的一種栅格化算法

除此之外還有一種算法是利用計算機圖形學中繪制直線的Bresenham算法,這種算法的效率很高,原理就是用周遊的辦法規避乘法和除法,隻用加減法就能完成線段的栅格化。

上述兩種算法有個問題就是都要經過一系列繁複的判斷,才能得到比較嚴密的結果,是以我并沒有采用。我這裡采用的算法也是逐漸步進求值的辦法,隻不過不再沿着X或者Y方向求值,而是沿着射線方向步進。這裡的射線指的是從線段的起點開始,以1像素為步進機關,步進到線段的終點。因為線段的方向性問題,步進得到的點總會有重複的值,最後再進行去重操作即可。

算法過程簡述如下:

  1. 設線段的起點為\(O\),終點為\(E\),則方向向量為\(D=E-O\);
  2. 線段的長度L為向量\(D\)的模。以0為初值,L為終值,以1為步進值建立一個for循環,每次取的長度為d;
  3. 令\(t=d/L\),則線段上相應的點為\(P=O+tD\)。這個公式是根據射線向量方程推導出來的,可以參看這篇文章《已知線段上某點與起點的距離,求該點的坐标》;
  4. 将取的點都儲存到容器中;
  5. 對容器中的點進行去重操作。

最終得到的點即為直線栅格化後的點。

具體的C++實作代碼如下:

#include <iostream>
#include <vector>

using namespace std;

const double EPSILON = 0.000001;

// 2D Point
struct Vector2d
{
public:
	Vector2d()
	{
	}

	Vector2d(double dx, double dy)
	{
		x = dx;
		y = dy;
	}

	// 矢量指派
	void set(double dx, double dy)
	{
		x = dx;
		y = dy;
	}

	// 矢量相加
	Vector2d operator + (const Vector2d& v) const
	{
		return Vector2d(x + v.x, y + v.y);
	}

	// 矢量相減
	Vector2d operator - (const Vector2d& v) const
	{
		return Vector2d(x - v.x, y - v.y);
	}

	//矢量數乘
	Vector2d Scalar(double c) const
	{
		return Vector2d(c*x, c*y);
	}

	// 矢量點積
	double Dot(const Vector2d& v) const
	{
		return x * v.x + y * v.y;
	}

	//向量的模
	double Mod() const
	{
		return sqrt(x * x + y * y);
	}

	bool Equel(const Vector2d& v) const
	{
		if (abs(x - v.x) < EPSILON && abs(y - v.y) < EPSILON)
		{
			return true;
		}
		return false;
	}

	double x, y;
};

//栅格化一條線段
void RasterLine(std::pair<Vector2d, Vector2d> line, std::vector<Vector2d>& linePointList)
{
	Vector2d vecLine = line.second - line.first;
	double lineLength = vecLine.Mod();
	double step = 1.0;

	//根據距離逐漸取
	vector<Vector2d> tmpPointList;
	double curLength = 0;
	while (curLength < lineLength)
	{
		curLength = curLength + step;
		Vector2d P = line.first + vecLine.Scalar(curLength / lineLength);
		P.x = (int)(P.x + 0.5);
		P.y = (int)(P.y + 0.5);
		tmpPointList.push_back(P);
	}

	//與最後一個值比較,去重
	linePointList.push_back(line.first);
	for (size_t i = 0; i < tmpPointList.size(); i++)
	{
		//與最後一個值比較,去重
		if (!tmpPointList[i].Equel(linePointList[linePointList.size() - 1]))
		{
			linePointList.push_back(tmpPointList[i]);
		}
	}

	if (!linePointList[linePointList.size() - 1].Equel(line.second))
	{
		linePointList.push_back(line.second);
	}
}


int main()
{
	Vector2d O(30, 60);
	Vector2d E(88, 104);
	std::pair<Vector2d, Vector2d> line(O, E);

	vector<Vector2d> linePointList;
	RasterLine(line, linePointList);

	for (size_t i = 0; i < linePointList.size(); i++)
	{
		cout << linePointList[i].x << ',' << linePointList[i].y << '\t';
	}
}
           

其運作的結果如下:

矢量線的一種栅格化算法

[1].矢量資料栅格化

[2].Bresenham算法