美术馆定理三角划分 c++实现

美术馆定理

最少只需多少名保安便可监视任意一个形状为简单多边形的美术馆
在这里插入图片描述
在这里插入图片描述
首先必须知道只需要一个保安就可以监视凸多边形
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

问题转化

在这里插入图片描述
首先不考虑最优解,我们先考虑简单的情况,就是将简单多边形三角拆分成一个个三角形,每个三角形放一个保安那么肯定是满足这个问题的解。

优化

这么看来一些三角形可以被多个保安看到,那么这说明这不是最优解。

三角拆分

可以三角拆分的多边形必须满足下面这个条件:
在这里插入图片描述
简单凸多边形可以拆分为边数n,n-2个三角形
在这里插入图片描述
然后进行三染色问题的简化,最后可以得到n/3向下取整个三角形
在这里插入图片描述
把保安放在最少的颜色的那些个顶点上,即满足解法。
推出下面这个答案
在这里插入图片描述

三角拆分问题

在这里插入图片描述

在这里插入图片描述

对角线是由两个位于多边形内部的不连续的顶点组成
1.对角线的端点应该是多边形的顶点
2.对角线必须完全位于多边形内部
在这里插入图片描述
像上面两条线就不是对角线

Ear Clipping三角拆分算法

在这里插入图片描述
比方说,一个多边形的顶点是由三个连续的顶点构成的V1 v2 v3其中v2是凸顶点。
V1和V3之间的连线应为一条对角线。
如下图所示:
在这里插入图片描述
判断v2点在v1v3向量的相对位置,如果是左边那么v2就是凸点,否则就是凹点。

例子

在这里插入图片描述
但是可以看到如果按顺序去分割会出现相交点

优化

在这里插入图片描述

一旦连接则移除
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时H D不是凸点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果

在这里插入图片描述

伪代码

在这里插入图片描述
ps:自己想的一种优化算法

1.初始化一个stack,先往里面push 三个vertex
while(stack.size()>=3)
	2.top三个vertex 将倒数第三个vertex与栈顶的vertex组成向量,然后计算倒数第二个vertex的相对位置,
	3.if 如果倒数第二个vertex是在左边:
		那么重新push倒数第三个与栈顶的vertex,记录对角线。
		continue;
	  else:
		否则三个vertex按照之前顺序push
		push个新的vertex

多边形的数据结构表示

在这里插入图片描述
这里用简化版本

template <typename T, size_t dim>
	struct Vertex
	{
		yhaida::Vector<T, dim> point;
		Vertex* next;
		Vertex* prev;

		Vertex(yhaida::Vector<T, dim>& _point, Vertex* _next=nullptr, Vertex* _prev=nullptr)
			:point(_point), next(_next), prev(_prev)
		{
		}
	};

	typedef Vertex<float, DIM2> Vertex2d;
	typedef Vertex<float, DIM3> Vertex3d;

	template <typename T, size_t dim = DIM3>
	class Polygon
	{
		std::vector<Vertex<T, dim>*> vertex_list;
	public:
		Polygon(const std::vector<yhaida::Vector<T, dim> >& points)
		{
			const int size = points.size();
			if (size < 3)
				std::cout << "Not enough points to construct a polygon\n" << std::endl;

			for (auto _point : points)
			{
				vertex_list.push_back(new Vertex<T,dim>(_point));
			}
			for (size_t i = 0; i < size; i++)
			{
				vertex_list[i]->next = vertex_list[(i + 1) / size];
				if (i != 0)
					vertex_list[i]->prev = vertex_list[i - 1];
				else
					vertex_list[i]->prev = vertex_list[size - 1];
			}
		}

		std::vector< Vertex<T, dim>*> getVertices()
		{
			return vertex_list;
		}
	};

	typedef Polygon<float, DIM3> PolygonS3d;
	typedef Polygon<float, DIM2> PolygonS2d;

Ear check

1.检查顶点是否是个凸点
2.检查相邻两个顶点是否能连接对角线(主要是判断对角线是否在多边形内)
在这里插入图片描述
注意单独检查是否有交点不足判断对角线是否处于内部。
比如下图
在这里插入图片描述
判断步骤
在这里插入图片描述
在这里插入图片描述

static bool leftOrBeyond(const Point2d& a, const Point2d& b, const Point2d& c)
{
	int position = orientation2d(a, b, c);
	return (position == RELATIVE_POSITION::LEFT || position == RELATIVE_POSITION::BEYOND);
}
static bool left(const Point2d& a, const Point2d& b, const Point2d& c)
{
	return orientation2d(a, b, c) == RELATIVE_POSITION::LEFT;
}

static bool interiorCheck(const Vertex2d* v1, const Vertex2d* v2)
{
	if (leftOrBeyond(v1->point, v1->next->point, v1->prev->point)) {
		// v1 is vonvx vertex
		return left(v1->point, v2->point, v1->prev->point)
			&& left(v2->point, v1->point, v1->next->point);
	}
	// v1 is relex vertex
	return !(leftOrBeyond(v1->point, v2->point, v1->next->point)
		&& leftOrBeyond(v2->point, v1->point, v1->prev->point));
}

bool isDiagonal(const Vertex2d* v1, const Vertex2d* v2, PolygonS2d* poly)
{
	bool prospect = true;
	std::vector<Vertex2d*> vertices;

	//1.先把vertex存入vector中
	if (poly)
		vertices = poly->getVertices();
	else
	{
		auto vertex_ptr = v1->next;
		vertices.push_back((Vertex2d*)v1);
		while (vertex_ptr != v1)
		{
			vertices.push_back(vertex_ptr);
			vertex_ptr = vertex_ptr->next;
		}
	}

	//2.判断是否相交
	Vertex2d* current, * next;
	current = vertices[0];
	do
	{
		next = current->next;
		if (current != v1 && next != v1 && current != v2 && next != v2
			&& yhaida::Intersection(v1->point, v2->point, current->point, next->point))
		{
			prospect = false;
			break;
		}
		current = next;
	} while (current != vertices[0]);
	return prospect && interiorCheck(v1, v2) && interiorCheck(v2, v1);
}

算法

在这里插入图片描述

在这里插入图片描述

static bool  isConvex(Vertex2d* v0, Vertex2d* v1, Vertex2d* v2)
{
	return leftOrBeyond(v1->point, v2->point, v0->point);
}

static void initialize_ear_status(PolygonS2d* polygon)
{
	Vertex2d* v0, * v1, * v2;
	auto vertices = polygon->getVertices();
	v1 = vertices[0];
	do
	{
		v0 = v1->prev;
		v2 = v1->next;
		if (isConvex(v0, v1, v2))//判断是否是凸点
			v1->is_ear = isDiagonal(v0, v2);//是否是内对角线是通用函数
		v1 = v1->next;
	} while (v1 != vertices[0]);
}

void Triangulate_earclipping(PolygonS2d* poly, std::vector<Edge2d>& edge_list)
{
	initialize_ear_status(poly);
	auto vertex_list = poly->getVertices();
	int no_vertex_to_process = vertex_list.size();
	Vertex2d* v0, * v1, * v2, * v3, * v4;
	while (no_vertex_to_process > 3)
	{
		for (size_t i = 0; i < vertex_list.size(); i++)
		{
			v2 = vertex_list[i];
			if (v2->is_ear && !v2->is_processed)//v2应该被处理,并且还未被处理
			{
				v1 = v2->prev;
				v0 = v1->prev;

				v3 = v2->next;
				v4 = v2->next;

				edge_list.push_back(Edge2d(*v1, *v3));
				v2->is_processed = true;

				//删除v2
				v1->next = v3;
				v3->prev = v1;

				//更新
				if (isConvex(v1->prev, v1, v1->next))
					v1->is_ear = isDiagonal(v0, v3,nullptr);
				if (isConvex(v3->prev, v3, v3->next))
					v3->is_ear = isDiagonal(v1, v4, nullptr);

				no_vertex_to_process--;
				if (no_vertex_to_process <= 3)
					break;
			}
		}
	}
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yhaida

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值