C++ 凸包生成算法

由于我的极差记忆力,我打算把这个破玩意先记下来。因为以后会有改动(Delaunay三角网生成算法),我不想把一个好的东西改坏了。。。

好吧……

凸包生成算法,:
1.先在指定的宽(width)高(height)范围内生成一堆随机点;
  1.1. 生成N个不重复的正整数,使用洗牌算法让生成的数字不重复;
  1.2. 将每个数字分解成坐标。可以设想一个二维数组,每个数字依次填进数组内。那么,对于数字A来说,它能够生成的坐标则为:

`x = A % width;` `y = (A% width== 0) ? A / width : A / width+ 1);`
2. 对所有随机点进行排序;   2.1.找到 y 值最小的点`pointYmin`;   2.2. 计算每个离散点`pt`、`pointYmin`、和X轴组成角的弧度`radian`(或者角度`angle`也可以)。其中,点`pointYmin`为角心;   2.3. 依照上面计算出的弧度进行排序。 3. 开始找凸包   3.1.(我懒得写了……)https://www.cnblogs.com/aiguona/p/7232243.html   3.2. 大体上就是,首先离散点集之中入栈两个点,然后需要计算每三个点`ptA`(栈内点,次顶点), `ptO`(栈内点,栈顶点), `ptB`(栈外点,当前点)之间的逆时针夹角,如果夹角是小于180(π)的,那么则`ptB`入栈,否则 `ptO`出栈。直到所有离散点都计算完成。

BugFix:
1.一系列的在VS2017(v141)无法编译通过的问题(SDK版本:10.0.17763.0)。
这其中包括STL模板参数中不允许存在const、va_start函数中不允许有引用变量、for each - in关键字被否决等等问题。

Note:

  1. 重做,将成员函数改成了由index代表的点,而不是原来的存放点的原型。(2019.4.9)
  2. 懒病作祟,有些代码值得优化,但现在没有那么做。

以下是代码……C++11
项目选项为 多字符集

File: stdinc.h

#include <cmath>
#include <random>
#include <stdarg.h>
#include <string>
#include <exception>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cassert>
#include <windows.h>

#pragma once

#ifdef _DEBUG
	#define ErrorThrow(errMsg)  { LogThrow::ErrorOccur(errMsg, __FILE__, __LINE__);}
#else
	#define ErrorThrow(errMsg) {}
#endif // _DEBUG

#include <iostream>



static const double PI = 3.14159265358980/*...80... -> ...7932384626...*/;
static const double precision = 1000.0;
static const double doubleESP = 1e-008;

namespace LogThrow
{
	static void ErrorOccur(const std::string& errMsg, const std::string& fileName, int line)
	{
		std::string msg("[ERROR] " + errMsg + '\n' + "File: " + fileName + '\n' + "Line: " + std::to_string(line));
		std::logic_error(msg.c_str());
		MessageBox(NULL, msg.c_str(), NULL, MB_ICONINFORMATION | MB_OK);
		ExitProcess(0);
	}
};

namespace MyMathTools
{
	inline static bool LessThan(double a, double b)
	{
		if (a - b < doubleESP)
		{
			return true;
		}
		return false;
	}

	inline static bool IsEqual(double a, double b)
	{
		if (std::abs(a - b) <= doubleESP)
		{
			return true;
		}
		return false;
	}

};

File: Point2D.cpp

#include "stdinc.h"

#pragma once

//Point & Vector
template<typename T>
class Point2D
{
private:

public:
	typedef std::vector<Point2D<T>> Points;
	T x, y;

public:
	Point2D(T nx, T ny) :x(nx), y(ny)
	{
	}

	Point2D() :Point2D{ 0, 0 }
	{
	}

	Point2D(const Point2D& np) :Point2D{ np.x, np.y }
	{
	}

	void Init(T x, T y)
	{
		this->x = x;
		this->y = y;
	}

	void Transposition()
	{
		T total = x + y;
		x = total - x;
		y = total - y;
	}

	void operator= (const Point2D& np/*new point*/)
	{
		Init(np.x, np.y);
	}

	bool operator== (const Point2D& pointOther) const 
	{
		if ((pointOther.x == this->x) &&
			(pointOther.y == this->y))
		{
			return true;
		}
		return false;
	}

	friend std::ostream& operator<<(std::ostream& out, const Point2D& pt)
	{
		out << "(" << pt.x << "," << pt.y << ")";
		return out;
	}


public:
	//[min, max)
	/*const */Point2D& RandomPoint(int maxX, int maxY)
	{
		if (maxX <= 0 || maxY <= 0)
		{
			std::logic_error("The less then 0!");
		}
		std::random_device rd;
		std::mt19937 mt(rd());

		this->x = mt() % maxX;
		this->y = mt() % maxY;

		//I can modify the left value.
		return *this;
	}

	template<typename T1, typename T2>
	static double Distance(const Point2D<T1>& ptA, const Point2D<T2>& ptB)
	{
		double cutX = ptB.x - ptA.x;
		double cutY = ptB.y - ptA.y;
		return std::pow((cutX * cutX + cutY * cutY), 0.50);
	}

	template<typename T1>
	double Distance(const Point2D<T1>& ptOther)
	{
		return Point2D::Distance<T, T1>(*this, ptOther);
	}

	//Without repetition: Shuffle algorithm.
	static void RandomPoints(int count, int maxX, int maxY, std::vector<Point2D<int>>& __out points)
	{
		//auto LcmFunc = [maxX, maxY](int numA, int numB)
		//{
		//  int max = (maxX > maxY) ? maxX : maxY;
		//  int min = maxX + maxY - max;
		//  long lcm = -1;
		//  for (int i = 1; i <= min; ++i)
		//  {
		//      lcm = static_cast<long>(max * i);
		//      if (lcm % min == 0)
		//      {
		//          break;
		//      }
		//  }
		//  return lcm;
		//};

		//int lcm = static_cast<char32_t>(LcmFunc(maxX, maxY));
		//if (lcm == -1)
		//{
		//  return false;
		//}

		if (count >= maxX * maxY * 0.5)
		{
			ErrorThrow("Error Count.");
		}

		points.clear();

		auto GetX = [maxX](int num)
		{
			return num % maxX;
		};

		auto GetY = [maxX](int num)
		{
			return (num % maxX == 0) ? num / maxX : num / maxX + 1;
		};

		static std::random_device rd;
		static std::mt19937 mt(rd());
		const long total = maxX * maxY;

		std::vector<long> nums;
		nums.resize(total);
		points.resize(count);
		for (long i = 0; i < total; ++i)
		{
			nums[i] = i;
		}

		for (int i = 0; i < count; ++i)
		{
			//It is array nums index.
			long randomNum = mt() % (total - i) + i;
			std::swap(nums[i], nums[randomNum]);

			//Swap over, the num[i] is new random number.
			//Get the point.
			points[i] = { GetX(nums[i]), GetY(nums[i]) };
		}
	}
};

template<typename T>
class Vector2D final : public Point2D<T>
{
public:
	Vector2D() : Point2D<T>()
	{
	}

	Vector2D(T nx, T ny) : Point2D<T>(nx, ny)
	{
	}

	Vector2D(const Vector2D& np) :Point2D<T>(np)
	{
	}

	double Length() const
	{
		return std::pow((this->x * this->x + this->y * this->y), .5);
	}

	//Vector AB
	void Vector(const Point2D<T>& ptA, const Point2D<T>& ptB)
	{
		this->Init(ptB.x - ptA.x, ptB.y - ptA.y);
	}

	double Radian(const Vector2D& otherVec, bool isQuadrant = true) const
	{
		return Vector2D::Radian(*this, otherVec, isQuadrant);
	}

	//anti-clockwise angle.
	//If the return value less than 0, the Radian is less than π.
	static double Radian(const Vector2D& vec1, const Vector2D& vec2, bool isQuadrant = true)
	{
		if (isQuadrant)
		{
			//CHECK IT: 
			double radian = std::atan2(vec2.y, vec2.x) - std::atan2(vec1.y, vec1.x);        //return [-2π, 2π]
			if (radian < 0)
			{
				radian += 2 * PI;
			}
			return radian;
		}

		return std::acos(Vector2D::Dot(vec1, vec2) / (vec1.Length() * vec2.Length()));  //return [0, π]
	}

	double Angle(const Vector2D& otherVec, bool isQuadrant = true) const
	{
		return Vector2D::Angle(*this, otherVec, isQuadrant);
	}

	//The same as function Radian.
	static double Angle(const Vector2D& vec1, const Vector2D& vec2, bool isQuadrant = true)
	{
		return Vector2D::Radian(vec1, vec2, isQuadrant) / PI * 180;
	}

	T Dot(const Vector2D& otherVec) const
	{
		return Vector2D::Dot(*this, otherVec);
	}

	static T Dot(const Vector2D& vec1, const Vector2D& vec2)
	{
		return vec1.x * vec2.x + vec1.y * vec2.y;
	}

	T Cross(const Vector2D& otherVec) const
	{
		return Vector2D::Cross(*this, otherVec);
	}

	//2D Vector have no cross. But we can set the Z is 0. 
	//So , return a value is (0, 0, Ax*By - Ay*Bx);
	static T Cross(const Vector2D& vec1, const Vector2D& vec2)
	{
		return vec1.x*vec2.y - vec1.y*vec2.x;
	}

};

File: ConvexHull.h

#pragma once

//#include "Line.cpp"
#include "Point2D.cpp"
#include "stdinc.h"
#include "ObjectSign.h"

typedef ObjectSign<Point2D<int>> Points;

using PointNode = ONode<Point2D<int>>;

class ConvexHull final
{
private:
	Points m_points;
	Points m_CHpoints;

public:
	void operator= (const ConvexHull&) = delete;

	ConvexHull();
	ConvexHull(const Points& points);
	~ConvexHull();

public:
	//void TestPoints();

	//void AddPoint(const Point2D<int>& __in newPoint);
	//void AddRandomPoints(int count, int maxX, int maxY);
	void GetConvexHullPoints(Points& __out points);
	void GetAllPoints(Points& __out points);

	void Generate();

	void Clear();

private:
	void SortPoints();
};


File: ConvexHull.cpp

#include "ConvexHull.h"
#include <algorithm>

ConvexHull::ConvexHull()
{

}

ConvexHull::ConvexHull(const Points& points) : m_points(points)
{
	//this->~ConvexHull();
	//m_points = points;
}

ConvexHull::~ConvexHull()
{
	
}

//void ConvexHull::AddPoint(const Point2D<int>& __in newPoint)
//{
//	m_points.Add(newPoint);
//}

void ConvexHull::GetConvexHullPoints(Points& __out points)
{
	points = m_CHpoints;
}

void ConvexHull::GetAllPoints(Points& __out points)
{
	points = m_points;
}

////Without repetition: Shuffle algorithm.
//void ConvexHull::AddRandomPoints(int count, int maxX, int maxY)
//{
//	std::vector<Point2D<int>> pointsArray;
//	Point2D<int>::RandomPoints(count, maxX, maxY, pointsArray);
//
//	m_points.Clear();
//	for (int i = 0; i < count; ++i)
//	{
//		const Point2D<int>& eachpt = pointsArray.at(i);
//		m_points[i] = eachpt;
//	}
//}

void ConvexHull::SortPoints()
{
	if (m_points.Size() == 0)
	{
		ErrorThrow("Empty Points.");
		return;
	}

	//Selecting a point what Y min.
	Point2D<int> pointYmin;
	m_points.Get(0, pointYmin);
	for(int i = 0; i < m_points.Size(); i++)
	{
		if (!m_points.IsExist(i))
		{
			continue;
		}

		const Point2D<int>& eachpt = m_points[i];
		if (eachpt.y < pointYmin.y)
		{
			pointYmin = eachpt;
		}
		else if (eachpt.y == pointYmin.y)
		{
			if (eachpt.x < pointYmin.x)
			{
				pointYmin = eachpt;
			}
		}
	}

	auto SortRuleFunc = [&pointYmin](const ONode<Point2D<int>>* pnA, const ONode<Point2D<int>>* pnB) -> bool
	{
		static const Vector2D<int> baseVecX = { 1, 0 };
		Point2D<int> ptA = pnA->_node;
		Point2D<int> ptB = pnB->_node;

		Vector2D<int> vecA;
		vecA.Vector(const_cast<const Point2D<int>&>(pointYmin), ptA);

		Vector2D<int> vecB;
		vecB.Vector(pointYmin, ptB);

		double radianA = Vector2D<int>::Radian(baseVecX, vecA);
		double radianB = Vector2D<int>::Radian(baseVecX, vecB);

		//If collinear occurs, the nearest is before.
		if (std::abs(radianA - radianB) <= doubleESP)
		{
			return vecA.Length() < vecB.Length();
		}

		//return (radianA - radianB) < doubleESP;   //ERROR: ATTENTION: Check the Strict Weak Ordering...
		return radianA < radianB;       //Ascending order.
	};

	m_points.Sort(SortRuleFunc);
}

void ConvexHull::Generate()
{
	const int pointsCount = static_cast<int>(m_points.Size());
	if (pointsCount < 3)
	{
		ErrorThrow("Points count too less.");
		return;
	}

	SortPoints();

	std::stack<PointNode> stackCHpt;
	stackCHpt.push(*(m_points.GetBySequence(0)));
	stackCHpt.push(*(m_points.GetBySequence(1)));

	int ptIndex = 2;    //Total is m_points.size().

	while (ptIndex < pointsCount)
	{
		PointNode pnO = stackCHpt.top();
		const Point2D<int>& ptO = pnO._node;

		stackCHpt.pop();
		PointNode pnA = stackCHpt.top();
		const Point2D<int>& ptA = pnA._node;

		stackCHpt.push(pnO);

		PointNode pnB = *(m_points.GetBySequence(ptIndex));
		Point2D<int> ptB = pnB._node;

		Vector2D<int> vecA, vecB;
		vecA.Vector(ptO, ptA);
		vecB.Vector(ptO, ptB);

		//Vector B turn to Vector A.
		double angle = Vector2D<int>::Angle(vecB, vecA);
		if ((angle - 0 >= 0) && (angle - 180 <= doubleESP))
		{
			stackCHpt.push(pnB);
			ptIndex++;
		}
		else
		{
			stackCHpt.pop(); //pop point O.
		}

		if (stackCHpt.size() < 2)
		{
			//If the sort rule is not ok...
			ErrorThrow("Error stackCHpt size.");
			return;
		}
	}
	/*Over Generation.*/

	//
	int lengthCHPoint = static_cast<int>(stackCHpt.size());
	m_CHpoints.Clear();
	for (int i = 0; i < lengthCHPoint; ++i)
	{
		const  PointNode& pn = stackCHpt.top();
		m_CHpoints.Add(pn);
		stackCHpt.pop();
	}
}

void ConvexHull::Clear()
{
	m_CHpoints.Clear();
	m_points.Clear();
}

//void ConvexHull::TestPoints()
//{
//	AddPoint({ 0, 0 });
//	AddPoint({ 5, 1 });
//	AddPoint({ 9, 2 });
//	AddPoint({ 1, 1 });
//	AddPoint({ 13, 6 });
//	AddPoint({ 12, 7 });
//}

File: Line.cpp (暂时没用到)

#include "Point2D.cpp"
#include <array>
#include <iostream>

#pragma once

template<typename T>
class Line
{
public:
	struct Equation
	{
	public:
		T A, B, C;

	public:
		//Ax + By + C = 0
		Equation()
		{
		}

		Equation(const Point2D<T>& ptA, const Point2D<T>& ptB)
		{
			(*this)(ptA, ptB);
		}
	public:
		void operator() (const Point2D<T>& ptA, const Point2D<T>& ptB)
		{
			A = ptB.y - ptA.y;
			B = ptA.x - ptB.x;
			C = ptB.x * ptA.y - ptA.x * ptB.y;
		}

		double GetY(double X)const
		{
			if (B == 0)
			{
				return INT_MAX;
			}

			return -1.0 * (C + A * X) / B;
		}

		double GetX(double Y) const
		{
			if (A == 0)
			{
				return INT_MAX;
			}

			return -1.0 * (C + B * Y) / A;
		}

		void GetSlope(Vector2D<double>& __out slope) const
		{
			if (B == 0) //X === -1.0 * C / A;
			{
				slope = { 0, -1.0 * C / A };
				return;
			}
			slope = { 1, -1.0 * A / B };
		}
	};

private:
	bool CheckLine(const Point2D<T>& ptA, const Point2D<T>& ptB) const
	{
		if (std::abs(ptA.x - ptB.x) <= doubleESP &&
			std::abs(ptA.y - ptB.y) <= doubleESP)
		{
			return false;
		}
		return true;
	}

public:
	Line() : Line({ 0, 0 }, { 1, 0 })
	{
	}

	Line(const Point2D<T>& a, const Point2D<T>& b) : ptA(a), ptB(b)
	{
		if (!CheckLine(a, b))
		{
			ErrorThrow("They are the same point.");
			return;
		}
		equation(a, b);
	}

	Line(const Line& nl) :Line{ nl.ptA, nl.ptB }
	{
	}

	Line(T xA, T yA, T xB, T yB) :Line({ xA, yA }, { xB, yB })
	{

	}

	void Init(const Point2D<T>& a, const Point2D<T>& b)
	{
		if (!CheckLine(a, b))
		{
			ErrorThrow("They are the same point.");
			return;
		}
		ptA = a;
		ptB = b;
		equation(a, b);
	}

	void Init(T xA, T yA, T xB, T yB)
	{
		if (!CheckLine({ xA, yA }, { xB, yB }))
		{
			ErrorThrow("They are the same point.");
			return;
		}
		ptA = { xA, yA };
		ptB = { xB, yB };
		equation(ptA, ptB);
	}

	void TransVector(Vector2D<T>& __out vec, bool isReverse/*B -> A*/ = false) const
	{
		if (isReverse) /*B -> A*/
			vec.Init(ptA.x - ptB.x, ptA.y - ptB.y);
		else /*A -> B*/
			vec.Init(ptB.x - ptA.x, ptB.y - ptA.y);
	}

	void operator= (const Line& nl)
	{
		Init(nl.ptA, nl.ptB);
	}

	void Midperpendicular(Line<double>& __out resLine) const
	{
		Point2D<double> midPoint = { 0.5 * (GetPointA().x + GetPointB().x), 0.5 * (GetPointA().y + GetPointB().y) };

		Vector2D<double> slope;
		equation.GetSlope(slope);

		Point2D<double> ptA, ptB;
		if (0 == slope.x)
		{
			//Perpendicular to the X-axis.
			ptA = { midPoint.x - 1.0, midPoint.y };
		}
		else
		{
			double k2 = -1.0 / (slope.y / slope.x);
			double b2 = midPoint.y - midPoint.x * k2;
			ptA = { midPoint.x + 1.0, k2 * (midPoint.x + 1.0) + b2 };   //Y = kX + b;
		}
		ptB = { midPoint.x, midPoint.y };

		resLine.Init(ptA, ptB);
	}

	static bool Intersection(const Line& __in lineA, const Line& __in lineB, Point2D<double>& __out pt)
	{
		if (Line::IsCollinear(4, lineA.ptA, lineA.ptB, lineB.ptA, lineB.ptB) == true)
		{
			return false;
		}

		T aA, aB, aC;
		T bA, bB, bC;

		aA = lineA.equation.A;
		aB = lineA.equation.B;
		aC = lineA.equation.C;

		bA = lineB.equation.A;
		bB = lineB.equation.B;
		bC = lineB.equation.C;

		double k = static_cast<double>(aA * bB - aB * bA);
		if (MyMathTools::IsEqual(k, 0.0))
		{
			return false;
		}
		pt = { (bB* -1 * aC - (-1) * bC * aB) / k, (aA * -1 * bC - (-1) * aC * bA) / k };
		return true;
	}

	void Intersection(const Line& __in lineOther, Point2D<double>& __out pt) const
	{
		Line::Intersection(*this, lineOther, pt);
	}

	static bool IsCollinear(int ptCount, const Point2D<T>& pt1, const Point2D<T>& pt2, const Point2D<T>& pt3, ...)
	{
		Vector2D<T> vecAB, vecBC;
		vecAB.Vector(pt1, pt2);
		vecBC.Vector(pt2, pt3);
		if (!MyMathTools::IsEqual(vecAB.x * 1.0 / vecBC.x, vecAB.y * 1.0 / vecBC.y))
		{
			return false;
		}

		//va_start argument must not have reference type
		auto tmppt = pt3;

		va_list aps;
		va_start(aps, tmppt);
		const Point2D<T>* pNextArg = &pt3;
		bool res = true;
		while (ptCount - 3 > 0)
		{
			const Point2D<T> ptX = va_arg(aps, const Point2D<T>);
			Vector2D<T> vecAx;
			vecAx.Vector(pt1, ptX);
			if (!MyMathTools::IsEqual(vecAB.x * 1.0 / vecAx.x, vecAB.y * 1.0 / vecAx.y))
			{
				res = false;
				break;
			}
			--ptCount;
		}

		va_end(aps);
		return res;
	}

	const Point2D<T>& GetPointA() const
	{
		return ptA;
	}

	const Point2D<T>& GetPointB() const
	{
		return ptB;
	}

private:
	Point2D<T> ptA, ptB;

public:
	Equation equation;
};

File: Triangle.h (暂时没用到)

#pragma once

#include "Point2D.cpp"
#include "Line.cpp"
#include "Circle.cpp"

class Triangle final
{
public:
	enum class EnumPTRelation: int 
	{
		POINT_OUTSIDE = -1,
		POINT_INSIDE = 0,
		POINT_ON_VERTICE = 1,
		POINT_ON_LINE = 2
	};

	Triangle();
	Triangle(const Point2D<int>& ptA, const Point2D<int>& ptB, const Point2D<int>& ptC);

	~Triangle();

public:
	void Init(const Point2D<int>& ptA, const Point2D<int>& ptB, const Point2D<int>& ptC);

	//RelationPT: The Relation of Point and Triangle.
	EnumPTRelation RelationPT(const Point2D<int>& pt) const;	
	void Circumcircle(Circle<double, double>& __out circle) const;
	double Area() const;

	void GetVertices(std::array<Point2D<int>, 3>& __out vs) const;
	void GetLines(std::array<Line<int>, 3>& __out ls) const;

private:
	std::array<Point2D<int>, 3> m_vertices /*m_vertex*/;
	std::array<Line<int>, 3> m_lines;
};

File: Triangle.cpp (暂时没用到)

#include "Triangle.h"

Triangle::Triangle() :Triangle({ 0, 0 }, { 0, 3 }, { 4, 0 })
{
}

Triangle::Triangle(const Point2D<int>& ptA, const Point2D<int>& ptB, const Point2D<int>& ptC)
{
	Init(ptA, ptB, ptC);
}

Triangle::~Triangle()
{
}

void Triangle::Circumcircle(Circle<double, double>& __out circle) const
{
	const Point2D<int> ptA = m_vertices[0];
	const Point2D<int> ptB = m_vertices[1];
	const Point2D<int> ptC = m_vertices[2];
	
	double A1, B1, C1, A2, B2, C2;
	A1 = 2.0 * (ptB.x - ptA.x);
	B1 = 2.0 * (ptB.y - ptA.y);
	C1 = ptB.x * ptB.x + ptB.y * ptB.y - ptA.x * ptA.x - ptA.y *ptA.y;

	A2 = 2.0 * (ptC.x - ptB.x);
	B2 = 2.0 * (ptC.y - ptB.y);
	C2 = ptC.x * ptC.x + ptC.y * ptC.y - ptB.x * ptB.x - ptB.y * ptB.y;

	circle.center.x = ((C1*B2) - (C2*B1)) / ((A1*B2) - (A2*B1));
	circle.center.y = ((A1*C2) - (A2*C1)) / ((A1*B2) - (A2*B1));
	circle.radius = std::pow((ptA.x - circle.center.x)*(ptA.x - circle.center.x) + (ptA.y - circle.center.y)*(ptA.y - circle.center.y), 0.5);
}

double Triangle::Area() const
{
	Vector2D<int> vecBA, vecBC;
	m_lines[0].TransVector(vecBA, true);
	m_lines[1].TransVector(vecBC);

	return 0.5 * Vector2D<int>::Cross(vecBA, vecBC);
}

void Triangle::GetVertices(std::array<Point2D<int>, 3>& vs) const
{
	vs = m_vertices;
}

void Triangle::GetLines(std::array<Line<int>, 3>& ls) const
{
	ls = m_lines;
}

void Triangle::Init(const Point2D<int>& ptA, const Point2D<int>& ptB, const Point2D<int>& ptC)
{
	if (Line<int>::IsCollinear(3, ptA, ptB, ptC))
	{
		ErrorThrow("It is Collinear.");
		return;
	}
	m_vertices[0] = ptA;
	m_vertices[1] = ptB;
	m_vertices[2] = ptC;

	m_lines[0].Init(ptA, ptB);
	m_lines[1].Init(ptB, ptC);
	m_lines[2].Init(ptC, ptA);
}

Triangle::EnumPTRelation Triangle::RelationPT(const Point2D<int>& pt) const
{
	Vector2D<int> vecPA, vecPB, vecPC;
	int64_t v1, v2, v3;

	vecPA.Vector(pt, m_vertices[0]);
	vecPB.Vector(pt, m_vertices[1]);
	vecPC.Vector(pt, m_vertices[2]);

	v1 = vecPA.Cross(vecPB);
	v2 = vecPB.Cross(vecPC);
	v3 = vecPC.Cross(vecPA);

	if (v1 * v2 * v3 == 0)
	{
		return EnumPTRelation::POINT_ON_VERTICE;	//The point and vertex coincidence.
	}

	//type long long, Do not use 'int'.
	int64_t relationR12 = v1 * v2;
	int64_t relationR13 = v1 * v3;
	if (relationR12 == 0 || relationR13 == 0)
	{
		return EnumPTRelation::POINT_ON_LINE; //The Point on the line.
	}

	if (relationR12 > 0 && relationR13 > 0)
	{
		return EnumPTRelation::POINT_INSIDE;	//The points inside triangle.
	}

	return EnumPTRelation::POINT_OUTSIDE;
}

File: Circle.cpp (暂时没用到)

#pragma once
#include "Point2D.cpp"

template<typename TC, typename TR>
class Circle final
{
public:
	Circle() :Circle({ 0,0 }, 1)
	{

	}

	Circle(const Point2D<TC>& centerPt, TR radius) :
		center(centerPt),
		radius(radius)
	{

	}

	double Area()
	{
		return PI * radius * radius;
	}

	double Perimeter()
	{
		return 2 * PI * radius;
	}

	template<typename TP>
	bool IsPointInCircle(const Point2D<TP>& pt)
	{
		return !MyMathTools::LessThan(radius, Point2D<TP>::Distance(center, pt));
	}


public:
	TR radius;
	Point2D<TC> center;
};

File: ObjectSign.h (命名不太好)

#pragma once

#include <unordered_map>
#include <set>
#include <functional>

#include "stdinc.h"

template <typename T>
class ONode
{
public:
	unsigned int _index;
	T _node;
	
private:

public:
	ONode()
	{
	}

	ONode(unsigned int index, const T& node)
	{
		Init(index, node);
	}

	ONode(const ONode<T>& other) :
		ONode(other._index, other._node)
	{
	}

	~ONode()
	{
	}

public:
	void Init(unsigned int index, const T& node) noexcept
	{
		if (static_cast<int>(index) > (INT_MAX >> 1))
		{
			ErrorThrow("The Index Is Too Large.");
			return;
		}

		_index = index;
		_node = node;
	}

	static ONode<T>* Alloc(unsigned int index, const T& node) noexcept
	{
		ONode<T>* ret;
		ret = new (std::nothrow) ONode<T>(index, node);

		return ret;
	}

	static void Free(ONode<T>*& p)
	{
		if (nullptr != p)
		{
			delete p;
			p = nullptr;
		}
	}
};

template <typename T>
class ObjectSign
{
public:
	ObjectSign()
	{
		Init();
	}

	ObjectSign(const ObjectSign& other)
	{
		Init(other);
	}

	~ObjectSign()
	{
		//Clear();
	}

	ObjectSign<T>& operator=(const ObjectSign& other)
	{
		this->Init(other);
		return *this;
	}

public:
	bool IsExist(unsigned index) const 
	{
		return !(_relation.end() == _relation.find(index));
	}

	int Add(const T& ele)
	{
		const ONode<T> node(++_max, ele);
		return (Add(node)) ? _max : -1;
	}

	bool Add(const ONode<T>& node)
	{
		if (IsExist(node._index)) 
			return false;

		ONode<T>* pNewNode = ONode<T>::Alloc(node._index, node._node);
		if (pNewNode == nullptr)
		{
			return false;
		}

		_max = (_max < node._index) ? node._index : _max;
		if (!_lackIndex.empty())
		{
			std::set<size_t>::iterator lackIt = _lackIndex.begin();
			size_t index = *lackIt;

			_elements[index] = pNewNode;
			_relation.insert(std::make_pair(node._index, index));
			_lackIndex.erase(lackIt);

			return true;
		}
		
		_elements.push_back(pNewNode);
		_relation.insert(std::make_pair(node._index, _elements.size() - 1));
		return true;
	}

	bool Del(unsigned int index)
	{
		std::unordered_map<unsigned int, size_t>::iterator itRa = _relation.find(index);
		if (itRa == _relation.end())
		{
			return false;
		}

		size_t n = itRa->second;
		if (_elements[n]->_index != index)
		{
			ErrorThrow("Del Function Error! The Index Not Match.");
			return false;
		}
		_relation.erase(itRa);
		ONode<T>::Free(_elements[n]);
		_lackIndex.insert(n);
		
		return true;
	}

	bool Change(unsigned int index, const T& node)
	{
		std::unordered_map<unsigned int, size_t>::iterator itRa = _relation.find(index);
		if (itRa == _relation.end())
		{
			return false;
		}

		_elements[itRa->second]->_node = node;
		return true;
	}

	bool Get(unsigned int index, T& __out res) const
	{
		std::unordered_map<unsigned int, size_t>::const_iterator itRa = _relation.find(index);
		if (itRa == _relation.end())
		{
			return false;
		}

		res = _elements[itRa->second]->_node;
		return true;
	}

	const T& Get(unsigned int index) const
	{
		static T res;
		if (false == Get(index, res))
		{
			ErrorThrow("Not Found The Index.");
		}

		return res;
	}

	//Get element by index.
	T& operator[](unsigned int index)
	{
		if (!IsExist(index))
		{
			T tmp;
			ONode<T>* pNewNode = ONode<T>::Alloc(index, tmp);
			Add(*pNewNode);
		}
		return this->_elements[_relation[index]]->_node;
	}
	
	const T& operator[](unsigned int index) const 
	{
		return Get(index);
	}

	//Get element by sequence.
	T& operator()(unsigned int sequence)
	{
		return GetBySequence(sequence)->_node;
	}

	const T& operator()(unsigned int sequence) const
	{
		ObjectSign* cthis = const_cast<ObjectSign*>(this);
		return cthis->GetBySequence(sequence)->_node;
	}

	const ONode<T>*const GetBySequence(unsigned int sequence) const
	{
		ObjectSign* cthis = const_cast<ObjectSign*>(this);
		return cthis->GetBySequence(sequence);
	}

	ONode<T>* GetBySequence(unsigned int sequence)
	{
		if (sequence >= _relation.size())
		{
			ErrorThrow("The sequence too large.");
			return nullptr;
		}

		if (_lackIndex.empty())
		{
			return _elements[sequence];
		}
		else
		{
			int lackCount = 0;
			for (std::set<size_t>::iterator it = _lackIndex.lower_bound(sequence); it != _lackIndex.begin(); --it)
			{
				lackCount++;
			}

			if (&*(_lackIndex.begin()) == nullptr)
			{
				lackCount += 1;
			}
			
			for (int i = sequence + lackCount; i < _elements.size(); i++)
			{
				ONode<T>* each = _elements[i];
				if (each == nullptr)
				{
					continue;
				}
				return _elements[i];
			}

			ErrorThrow("The element is not found. sequence is " + std::to_string(sequence) + "; Size is " + std::to_string(_relation.size()) + " .");
			return nullptr;
		}
	}

	void Clear()
	{
		if (_relation.empty())
		{
			return;
		}

		std::unordered_map<unsigned int, size_t>::iterator it = _relation.begin();
		for (; it != _relation.end(); it++)
		{
			ONode<T>::Free(_elements[it->second]);
		}

		_elements.clear();
		_relation.clear();
		_lackIndex.clear();
	}

	size_t Size() const
	{
		return _relation.size();
	}

	void Sort(
		const std::function<bool(const ONode<T>* A, const ONode<T>* B)>& SortRuleFunc)
	{	
		std::vector<ONode<T>*> sortRes;
		sortRes.reserve(_elements.size());
		_lackIndex.clear();
		_relation.clear();

		for (auto it : _elements)
		{
			ONode<T>* pEach = &*it;
			if (pEach == nullptr)
			{
				continue;
			}

			sortRes.push_back(pEach);
		}

		std::sort(sortRes.begin(), sortRes.end(), SortRuleFunc);
		_elements.clear();

		_elements = sortRes;
		size_t idx = 0;
		for (auto it : _elements)
		{
			ONode<T>* pEach = &*it;
			_relation.insert(std::make_pair(pEach->_index, idx));
			++idx;
		}
	}

	private:
		void Init(int size = 0, bool isStrict = true)
		{
			Clear();
			_max = 0;
			_isStrict = isStrict;
			if (size <= 0)
			{
				return;
			}

			_elements.resize(size);
			for (int i = 0; i < size; ++i)
			{
				_lackIndex.insert(i);
			}
		}

		void Init(const ObjectSign& other)
		{
			this->Clear();

			this->_elements.assign(other._elements.begin(), other._elements.end());
			this->_lackIndex.insert(other._lackIndex.begin(), other._lackIndex.end());
			this->_relation.insert(other._relation.begin(), other._relation.end());
			this->_max = other._max;
			this->_isStrict = other._isStrict;
		}

private:
	std::vector<ONode<T>*> _elements;	

	std::unordered_map<unsigned int, size_t> _relation;
	std::set<size_t> _lackIndex;
	unsigned int _max;
	bool _isStrict;	//TODO: 
};

执行结果:

posted on 2018-10-10 19:23  __Even  阅读(3722)  评论(1编辑  收藏  举报

导航