使用耳切法将多边形三角化【转】

2023-05-16

https://blog.csdn.net/yiwei151/article/details/87946592

效果图:

 

做法及原理可参考此链接:http://www.cnblogs.com/xignzou/p/3721494.html

 代码:

using System;
using System.Collections.Generic;
using UnityEngine;

namespace PolygonTool
{

#region 耳切法对简单多边形进行三角形化

/// <summary>
/// 判断凹点,凸点,耳朵的比较轴
/// </summary>
public enum CompareAxle
{
X,
Y,
Z
}

/// <summary>
/// 对多边形处理
/// </summary>
public class Triangulation
{

/// <summary>
/// 判断凹凸的时候的比对轴
/// </summary>
private CompareAxle _compareAxle = CompareAxle.Y;

/// <summary>
/// 多边形顶点
/// </summary>
private List<Vector3> _polygonVertexs = new List<Vector3>();

/// <summary>
/// 顶点序列
/// </summary>
private List<int> _vertexsSequence = new List<int>();

/// <summary>
/// 节点管理器
/// </summary>
private NodeManager _nodeManager = new NodeManager();

/// <summary>
/// 初始化
/// </summary>
/// <param name="polygonVertexs">多边形顶点</param>
public Triangulation(List<Vector3> polygonVertexs)
{
this._polygonVertexs = polygonVertexs;
_nodeManager.Init(polygonVertexs);
}

/// <summary>
/// 设置比较轴
/// </summary>
/// <param name="compareAxle"></param>
public void SetCompareAxle(CompareAxle compareAxle)
{
this._compareAxle = compareAxle;
}

/// <summary>
/// 获取三角形的顶点序列
/// </summary>
public int[] GetTriangles()
{
while (_nodeManager.LinkedListLength >= 3)
{
SplitResult sr = SplitPolygon();
//
if (sr == null)
{
Debug.Log("null");
return null;
}
}

return _vertexsSequence.ToArray();
}

/// <summary>
/// 计算凹顶点,凸顶点,耳朵
/// </summary>
private SplitResult SplitPolygon()
{
//凹点
List<Node> _concaveVertexs = new List<Node>();
//凸点
List<Node> _raisedVertexs = new List<Node>();
//耳朵
List<Node> _polygonEars = new List<Node>();
//起始节点
Node currentNode = _nodeManager.FirstNode;

#region 计算凹顶点,凸顶点

for (int i = 0; i < _nodeManager.LinkedListLength; i++)
{
Vector3 one = currentNode.vertex - currentNode.lastNode.vertex;
Vector3 two = currentNode.nextNode.vertex - currentNode.vertex;
Vector3 crossRes = Vector3.Cross(one, two);

if (_compareAxle == CompareAxle.Y)
{
if (crossRes.y > 0)
{
_concaveVertexs.Add(currentNode);
}
else
{
_raisedVertexs.Add(currentNode);
}
}

if (_compareAxle == CompareAxle.X)
{
if (crossRes.x > 0)
{
_concaveVertexs.Add(currentNode);
}
else
{
_raisedVertexs.Add(currentNode);
}
}

if (_compareAxle == CompareAxle.Z)
{
if (crossRes.z > 0)
{
_concaveVertexs.Add(currentNode);
}
else
{
_raisedVertexs.Add(currentNode);
}
}

_polygonEars.Add(currentNode);
currentNode = currentNode.nextNode;
}

for (int i = 0; i < _concaveVertexs.Count; i++)
{
_polygonEars.Remove(_concaveVertexs[i]);
}
#endregion

#region 计算耳朵
List<int> needRemoveIdList = new List<int>();
for (int i = 0; i < _polygonEars.Count; i++)
{
Node earNode = _polygonEars[i];
Node compareNode = earNode.nextNode.nextNode;

while (compareNode != earNode.lastNode)
{
bool isIn = IsIn(compareNode.vertex, earNode.lastNode.vertex, earNode.vertex,
earNode.nextNode.vertex);

if (isIn == true)
{
if (_polygonEars.Contains(_polygonEars[i]))
{
needRemoveIdList.Add(_polygonEars[i].id);
}
break;
}
compareNode = compareNode.nextNode;
}
}

for (int j = 0; j < needRemoveIdList.Count; j++)
{
for (int i = 0; i < _polygonEars.Count; i++)
{
if (_polygonEars[i].id == needRemoveIdList[j])
{
_polygonEars.RemoveAt(i);
}
}
}

#endregion

#region 打印初始化结果

Debug.Log("凸点");
for (int i = 0; i < _raisedVertexs.Count; i++)
{
Debug.Log(_raisedVertexs[i].id);
}

Debug.Log("凹点");
for (int i = 0; i < _concaveVertexs.Count; i++)
{
Debug.Log(_concaveVertexs[i].id);
}

Debug.Log("耳朵");
for (int i = 0; i < _polygonEars.Count; i++)
{
Debug.Log(_polygonEars[i].id);
}

Debug.Log("-----------------------------------------------");
#endregion

//耳朵为空说明不是简单多边形 多边形三角化失败
if (_polygonEars.Count == 0)
{
return null;
}

_vertexsSequence.Add(_polygonEars[0].lastNode.id);
_vertexsSequence.Add(_polygonEars[0].id);
_vertexsSequence.Add(_polygonEars[0].nextNode.id);
_nodeManager.RemoveNode(_polygonEars[0]);


return new SplitResult(_raisedVertexs, _concaveVertexs, _polygonEars);
}

/// <summary>
/// 判断一点是否在三角形内
/// </summary>
/// <param name="p">一点</param>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <returns></returns>
public bool IsIn(Vector3 p,Vector3 a,Vector3 b,Vector3 c)
{
Vector3 pa = p - a;
Vector3 pb = p - b;
Vector3 pc = p - c;

Vector3 t1 = Vector3.Cross(pa, pb);
Vector3 t2 = Vector3.Cross(pb, pc);
Vector3 t3 = Vector3.Cross(pc, pa);

bool isIn2 = t1.y >= 0 && t2.y >= 0 && t3.y >= 0 || t1.y <= 0 && t2.y <= 0 && t3.y <= 0;

return isIn2;
}

/// <summary>
/// 管理多边形 构成一个双向链表
/// </summary>
public class NodeManager
{

private List<Node> _nodeList = new List<Node>();

public int LinkedListLength
{
get { return _nodeList.Count; }
}

public Node FirstNode
{
get { return _nodeList[0]; }
}

public void Init(List<Vector3> vertexs)
{

for (int i = 0; i < vertexs.Count; i++)
{
Node node = new Node(i, vertexs[i]);
_nodeList.Add(node);
}

for (int i = 0; i < LinkedListLength; i++)
{
if (i == 0)
{
_nodeList[i].lastNode = _nodeList[LinkedListLength - 1];
_nodeList[i].nextNode = _nodeList[1];
}
else if (i == LinkedListLength - 1)
{
_nodeList[i].lastNode = _nodeList[LinkedListLength - 2];
_nodeList[i].nextNode = _nodeList[0];
}
else
{
_nodeList[i].lastNode = _nodeList[i - 1];
_nodeList[i].nextNode = _nodeList[i + 1];
}
}
}

public void RemoveNode(Node node)
{
_nodeList.Remove(node);
node.lastNode.nextNode = node.nextNode;
node.nextNode.lastNode = node.lastNode;
}
}

public class Node
{

public int id;
public Vector3 vertex;
public Node lastNode;
public Node nextNode;

public Node(int id, Vector3 vertex)
{
this.id = id;
this.vertex = vertex;
}

public Node(int id, Vector3 vertex, Node lastNode, Node nextNode)
{
this.id = id;
this.vertex = vertex;
this.lastNode = lastNode;
this.nextNode = nextNode;
}
}

public class SplitResult
{
/// <summary>
/// 凸顶点
/// </summary>
public List<Node> raisedVertexs;

/// <summary>
/// 凹顶点
/// </summary>
public List<Node> concaveVertexs;

/// <summary>
/// 耳朵
/// </summary>
public List<Node> polygonEars;

public SplitResult(List<Node> raisedVertexs, List<Node> concaveVertexs, List<Node> polygonEars)
{
this.raisedVertexs = raisedVertexs;
this.concaveVertexs = concaveVertexs;
this.polygonEars = polygonEars;
}
}
}

#endregion
}

 

github地址:https://github.com/yiwei151/PolygonTriangulation

 

转载于:https://www.cnblogs.com/mazhenyu/p/11577642.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用耳切法将多边形三角化【转】 的相关文章

随机推荐

  • PIL图像处理模块生成验证码

    Django学习第四天 验证码 因所学Django项目中含有登录模块 xff0c 于是乎想着现在基本所有的登录界面都需要验证码 xff0c 于是学习了一下如何生成验证码图片并将它返回到前端 span class token keyword
  • 【2021-11-14】Android Studio 总是报错 Unresolved Class ‘MainActivity‘ 的解决办法

    在 MainActivity 类对应的源文件 MainActivity java 或 MainActivity kt 开头 xff0c 不要漏掉 package 语句 例如 xff1a span class token keyword pa
  • abaqus中的约束

    1 tie 绑定约束 xff1a 作用是将模型的两部分区域绑定在一起 xff0c 二者之间不发生相对运动 xff0c 相当于焊在一起 2 rigid body 刚体约束 使一个模型区域刚体化 xff0c 这个区域可以是一系列节点 xff0c
  • $NOI2012$迷失游乐园

    今天不知道写什么当链接 换根 DP 43 基环树 xff0c 思路不难 xff0c 要考虑的东西很多 xff0c 因为我直接在环上跑编号 xff0c 所以细节更多 一篇很清晰的题解 code include lt bits stdc 43
  • 网络流

    最大流 61 最大独立集 61 最小点覆盖 转载于 https www cnblogs com gaudar p 11608786 html
  • 尺取

    区间连续时用 xff0c 单调性 xff0c 看看n 2的算法能不能优化为n 转载于 https www cnblogs com gaudar p 11608924 html
  • 帆软报表(finereport)大屏细节操作(持续更新)

    图表间之间的组件间隔 xff1a body gt 属性 gt 布局 gt 组件间隔 决策报表背景水印 xff1a body gt 属性 gt 水印 仪表盘指针 枢纽 背景颜色 样式 gt 系列 柱形图 组合图警戒线 xff1a 样式 gt
  • Matlab程序学习(散点边界线/包络线)

    针对散点边界线 xff0c Matlab重的convhull为凸包络线 xff0c 往往不能满足实际工作需要 作散点的紧致包络线 xff0c 需要考虑到凹凸点 xff0c 为减少包络线与散点数据的过度紧密 穿过全散点 xff0c 并不过度包
  • 【总结】matlab求两个序列的相关性

    首先说说自相关和互相关的概念 自相关 在统计学中的定义 xff0c 自相关函数就是将一个有序的随机变量系列与其自身作比较 每个不存在相位差的系列 xff0c 都与其都与其自身相似 xff0c 即在此情况下 xff0c 自相关函数值最大 在信
  • 创建一个ProtonMail邮箱账号,保护我们的隐私安全。

    ProtonMail 是哈佛 xff08 Harvard xff09 麻省理工 xff08 MIT xff09 以及 CERN xff08 欧洲核子物理研究所 xff09 的科学家合力研发推出的一项加密电子邮件服务 推荐的ProtonMai
  • 英语发音规则---字母组合oo的发音规律

    英语发音规则 字母组合oo的发音规律 一 总结 一句话总结 xff1a 在英语单词中 xff0c 字母组合oo多数读长音 u xff0c 少数读短音 另外 xff0c 还有极少数的特殊情况读 xff0c 在英语单词中 xff0c 字母组合o
  • [Target Connection]: Connected system ID hash not found on target at expecte 解决方法

    在NiOS ii 软核系统搭建时 xff0c 确认系统搭建无误并且已经连接板子的情况下 xff0c 点击run as gt nios ii hardware之后如果报错 xff1a Target Connection Connected s
  • 【C / C++】包含多个工程(项目)的解决方案中,正确将标识符定义后,仍出现关于该标识符的 LNK2019 链接错误的一种情况

    标识符所在的工程 xff08 项目 xff09 存在其它问题 xff0c 导致编译失败 xff0c 使得依赖该标识符的其它工程无法正确查找到该标识符的定义并链接 解决方法 xff1a 先解决此工程的其它编译问题
  • Docker启动时的报错汇总

    八个Docker常见故障 https mp weixin qq com s 2GNKmRJtBGHhUyVBRbRgeA 八个Docker常见故障 报错一 xff1a error initializing graphdriver Docke
  • linux服务器定时关机重启,Ubuntu Server 10.10 每天定时开关机

    Ubuntu Server 10 10定时开机方法 xff1a 按F2进入BIOS设置 xff0c 设置每天定时开机 容易出现问题 xff1a BIOS时间比系统时间慢8小时 在BIOS设置中设置时间或在Ubuntu系统中设置BIOS时间为
  • You're currently running Fcitx with GUI 错误解决 Fcitx

    在英文版ubuntu配置输入法时 xff0c 点击 Configure Current Input Method 会报以下的错误 xff1a You re currently running Fcitx with GUI but fcitx
  • Linux 日志查看常用命令

    日志是系统运行的重要文件 xff0c 当系统发生错误 xff0c 查看日志文件是非常有必要的 但是 xff0c 当文件过大时 xff0c 就不能用vi 进行全部查看 xff0c 需要相应的日志查看命令 如果想查看日志中的某几行 xff0c
  • Windows远程桌面Debian配置

    由于xrdp gnome和unity之间的兼容性问题 xff0c 在Debian仍然无法使用xrdp登陆gnome或unity的远程桌面 xff0c 现象是登录后只有黑白点为背景 xff0c 无图标也无法操作 使用xrdp只能登录xfce的
  • ASE高级软件工程 第一次结对作业

    黄金点游戏Bot Bot8前来报道 1 问题定义 a 问题描述 N个玩家 xff0c 每人写一个0 100之间的有理数 不包括0或100 xff0c 提交给服务器 xff0c 服务器在当前回合结束时算出所有数字的平均值 xff0c 然后乘以
  • 使用耳切法将多边形三角化【转】

    https blog csdn net yiwei151 article details 87946592 效果图 xff1a 做法及原理可参考此链接 xff1a http www cnblogs com xignzou p 3721494