数据结构与算法-耳切法处理多边形三角划分c#语言

外文地址Triangulation by Ear Clipping

译文地址耳切法处理多边形三角划分

这里是对此算法的应用

暂时不去考虑含有岛洞的多边形

这个算法主要考虑的点有以下几个:

  • 1.计算凹凸三角形
  • 2.计算点是否包含在三角形内
  • 3.计算点是否在三角形边上和上一个类似不过我暂时没有写到更好地方法

这是在unity里的效果图 不做共线判断的时候图一会出错  

上代码 暂时先这样 过几天等拿到数据之后大规模测试再验证是否完善

生成面的部分用到的是unity的api

using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 耳切法
/// </summary>
public class EarsCutAlgorithm 
{
	public static List<Vector3> GetTriangles(List<Vector3> points)
	{
        int sum  =0;
        int lenf = points.Count - 2;
        List<Vector3> Triangles = new List<Vector3>();
        int index = 0;
        for (int i = 0; i < lenf; i++)
        {
            sum++;
            if(sum>100)
            {
                Debug.Log("100次运算强制跳出"+ points.Count);
                break;
            }
            if (index >= points.Count-2)
            {
                if (points.Count > 3)
                {
                    Debug.Log("开始第二波");
                    i = 0;
                    index = 0;
                }
                else
                {
                    Debug.Log("结束跳出"+ sum);
                    break;
                }
            }
            Vector3 a = points[index + 1] - points[index];
            Vector3 b = points[index + 2] - points[index];
            switch (GetCross(a, b))
            {
                case 0://一条线 删掉中间的点
                    if (points.Count > 3)
                        points.RemoveAt(index + 1);
                    break;
                case 1://凹三角 继续下一个
                    index++;
                    continue;
                case -1://逆时针方向  加入
                    Vector3 center = points[index];
                    Vector3 left = points[index+1];
                    Vector3 right = points[index+2];
                    bool isOK = true;
                    foreach (var item in points)
                    {
                        if(InTrigon(item, center, left, right))//判断不在三角形内  true 就是在三角形内
                        {
                            //Debug.Log("在三角形内");
                            isOK = false;
                            break;
                        }
                        else
                        {
                            if (GetLine(center, left,item) ||
                              GetLine(left, right, item) ||
                             GetLine(right, center, item))
                            {
                                //Debug.Log("在边上");
                                isOK = false;
                                break;
                            }
                        }
                    }
                    if (isOK)
                    {
                        Triangles.Add(center);
                        Triangles.Add(left);
                        Triangles.Add(right);
                        CREATE(points[index], points[index + 1], points[index + 2]);
                        if (points.Count > 3)
                            points.RemoveAt(index + 1);
                    }
                    index++;
                    break;
            }
        }
        Debug.Log(points.Count);
        CREATE(points[0], points[1], points[2]);
        return Triangles;
	}
    static void CREATE(Vector3 a, Vector3 b, Vector3 c)
    {
        List<Vector3> top = new List<Vector3>() { a, b, c };
        List<int> tri = new List<int>() { 2, 1, 0 };
        Mesh mesh = new Mesh();
        mesh.vertices = top.ToArray();
        mesh.triangles = tri.ToArray();
        GameObject topchild = new GameObject("topchild");
        topchild.transform.SetParent(CreateBoxManager.ins.transform);
        topchild.transform.localPosition = Vector3.zero;
        //mesh.uv = uvs;
        MeshFilter meshf = topchild.AddComponent<MeshFilter>();
        meshf.mesh = mesh;
        MeshRenderer render = topchild.AddComponent<MeshRenderer>();
        render.material = CreateBoxManager.ins.ma;
    }
    /// <summary>
    /// 叉乘计算方向 判断凹凸三角形
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <returns></returns>
    static int GetCross(Vector3 a, Vector3 b)
    {
        float y = Vector3.Cross(a, b).y;
        if (y == 0) return 0;
        if (y > 0) return 1;
        else return -1;
    }
    //判断是否在线上  c点是否在ab线段上
    static bool GetLine(Vector3 a,Vector3 b,Vector3 c)
    {
        if (a == c || b == c) return false;//相同的点直接返回false
        if (Vector3.Distance(a, c) > Vector3.Distance(a, b)) return false;//同一条线但是不在ab线段上 在之外的延长线 也返回false
        float y = Vector3.Cross(b-a, c-a).y;
        return y == 0;
    }
    /// <summary>
    /// 判断target是否在center left right三个点形成的三角形范围内  在边上也是返回false
    /// </summary>
    public static bool InTrigon(Vector3 _target, Vector3 _center, Vector3 _left, Vector3 _right)
	{
		Vector3 Ctl = _left - _center;
		Vector3 Ctr = _right - _center;
		Vector3 Ctt = _target - _center;
		Vector3 Ltr = _right - _left;
		Vector3 Ltc = _right - _center;
		Vector3 Ltt = _left - _target;
		Vector3 Rtl = _left - _right;
		Vector3 Rtc = _center - _right;
		Vector3 Rtt = _target - _right;
        if (
		Vector3.Dot(Vector3.Cross(Ctl, Ctr).normalized, Vector3.Cross(Ctl, Ctt).normalized) == 1 &&
		Vector3.Dot(Vector3.Cross(Ltr, Ltc).normalized, Vector3.Cross(Ltr, Ltt).normalized) == 1 &&
		Vector3.Dot(Vector3.Cross(Rtc, Rtl).normalized, Vector3.Cross(Rtc, Rtt).normalized) == 1
		)
			return true;
		else
			return false;
	}
}

====================================================================================================

2020.5.13修改 关于共线判断 有问题  那个方法修改如下

  //判断是否在线上  c点是否在ab线段上
    static bool GetLine(Vector3 a,Vector3 b,Vector3 c)
    {
        if (a == c || b == c) return false;//相同的点直接返回false
        if (Vector3.Distance(a, c) > Vector3.Distance(a, b)) return false;//同一条线但是不在ab线段上 在之外的延长线 也返回false
        //两个向量的夹角  判断共线问题的话 就是如果夹角为180就是点在线段上  点c在ab线段上  ac向量和bc向量的角度 acb的角度
        Vector3 ac = a - c;
        Vector3 bc = b - c;
        double sin = ac.x * bc.z - bc.x * ac.z;
        double cos = ac.x * bc.x + ac.z * bc.z;
        return Math.Atan2(sin, cos) * (180 / Math.PI) == 180;
    }

可以搞定这种标准十字形的情况

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值