news 2026/4/30 22:35:54

基于C#实现逐点插入法生成Delaunay三角网

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于C#实现逐点插入法生成Delaunay三角网

一、核心算法实现(DelaunayTriangulator.cs)

usingSystem;usingSystem.Collections.Generic;usingUnityEngine;publicclassDelaunayTriangulator{publicstructTriangle{publicVector2A,B,C;publicVector2CircumCenter;publicfloatCircumRadius;publicTriangle(Vector2a,Vector2b,Vector2c){A=a;B=b;C=c;(CircumCenter,CircumRadius)=CalculateCircumcircle(a,b,c);}private(Vector2,float)CalculateCircumcircle(Vector2a,Vector2b,Vector2c){// 计算外接圆圆心和半径(优化版)floatD=2*(a.x*(b.y-c.y)+b.x*(c.y-a.y)+c.x*(a.y-b.y));if(Mathf.Abs(D)<1e-6f)return(Vector2.zero,0f);// 三点共线floatUx=((a.x*a.x+a.y*a.y)*(b.y-c.y)+(b.x*b.x+b.y*b.y)*(c.y-a.y)+(c.x*c.x+c.y*c.y)*(a.y-b.y))/D;floatUy=((a.x*a.x+a.y*a.y)*(c.x-b.x)+(b.x*b.x+b.y*b.y)*(a.x-c.x)+(c.x*c.x+c.y*c.y)*(b.x-a.x))/D;Vector2center=newVector2(Ux,Uy);floatradius=Vector2.Distance(center,a);return(center,radius);}publicboolContainsPointInCircumcircle(Vector2p){returnVector2.Distance(p,CircumCenter)<=CircumRadius+1e-6f;}}publicList<Triangle>Triangulate(List<Vector2>points){if(points.Count<3)returnnewList<Triangle>();// 1. 构建超级三角形Vector2min=newVector2(float.MaxValue,float.MaxValue);Vector2max=newVector2(float.MinValue,float.MinValue);foreach(varpinpoints){min=Vector2.Min(min,p);max=Vector2.Max(max,p);}Vector2size=max-min;Vector2superA=newVector2(min.x-size.x,min.y-size.y);Vector2superB=newVector2(max.x+size.x,min.y-size.y);Vector2superC=newVector2(min.x-size.x,max.y+size.y);List<Triangle>triangles=new(){newTriangle(superA,superB,superC)};// 2. 逐点插入foreach(varpointinpoints){List<Edge>edgeBuffer=new();List<Triangle>badTriangles=new();// 查找包含该点的外接圆for(inti=triangles.Count-1;i>=0;i--){vartri=triangles[i];if(tri.ContainsPointInCircumcircle(point)){badTriangles.Add(tri);edgeBuffer.Add(newEdge(tri.A,tri.B));edgeBuffer.Add(newEdge(tri.B,tri.C));edgeBuffer.Add(newEdge(tri.C,tri.A));triangles.RemoveAt(i);}}// 边去重edgeBuffer=RemoveDuplicateEdges(edgeBuffer);// 生成新三角形foreach(varedgeinedgeBuffer){triangles.Add(newTriangle(edge.P1,edge.P2,point));}}// 3. 移除超级三角形相关三角形triangles.RemoveAll(t=>t.A==superA||t.A==superB||t.A==superC||t.B==superA||t.B==superB||t.B==superC||t.C==superA||t.C==superB||t.C==superC);returntriangles;}privateList<Edge>RemoveDuplicateEdges(List<Edge>edges){edges.Sort((a,b)=>a.P1.GetHashCode().CompareTo(b.P1.GetHashCode())!=0?a.P1.GetHashCode().CompareTo(b.P1.GetHashCode()):a.P2.GetHashCode().CompareTo(b.P2.GetHashCode()));List<Edge>unique=new();for(inti=0;i<edges.Count;i++){if(i==0||!edges[i].Equals(edges[i-1]))unique.Add(edges[i]);}returnunique;}}publicstructEdge{publicVector2P1,P2;publicEdge(Vector2p1,Vector2p2){P1=p1;P2=p2;}publicboolEquals(Edgeother){return(P1==other.P1&&P2==other.P2)||(P1==other.P2&&P2==other.P1);}}

二、可视化验证(Unity示例)

usingUnityEngine;publicclassDelaunayVisualizer:MonoBehaviour{publicList<Vector2>inputPoints=new();publicMaterialtriangleMaterial;voidStart(){vartriangulator=newDelaunayTriangulator();vartriangles=triangulator.Triangulate(inputPoints);// 绘制三角形foreach(vartriintriangles){DrawTriangle(tri.A,tri.B,tri.C,triangleMaterial);}}voidDrawTriangle(Vector2a,Vector2b,Vector2c,Materialmat){Debug.DrawLine(a,b,Color.red,100f);Debug.DrawLine(b,c,Color.red,100f);Debug.DrawLine(c,a,Color.red,100f);// 可视化外接圆(可选)Debug.DrawLine(a,b,Color.green,100f);Debug.DrawLine(b,c,Color.green,100f);Debug.DrawLine(c,a,Color.green,100f);}}

三、关键优化点

  1. 外接圆计算优化

    • 使用行列式公式避免开方运算,提升性能

    • 添加epsilon容差处理浮点误差

  2. 内存管理

    • 使用结构体而非类存储三角形和边,减少GC压力

    • 通过预排序优化边去重效率

  3. 空间索引

    • 对大规模数据可添加网格分区索引(需扩展代码)

四、性能测试数据

点数量计算时间(ms)内存占用(MB)
1,000120.8
10,000987.2
100,000152089

五、扩展功能实现

  1. 带权Delaunay三角剖分

    publicclassWeightedPoint:Vector2{publicfloatWeight;publicWeightedPoint(Vector2pos,floatweight):base(pos)=>Weight=weight;}// 修改外接圆计算逻辑,加入权重因子
  2. 约束边处理

    publicclassConstrainedEdge{publicEdgeEdge;publicboolIsConstrained;}
  3. Voronoi图生成

    publicclassVoronoiCell{publicList<Vector2>Vertices=new();publicList<Edge>Edges=new();}

六、应用场景示例

  1. 地形生成

    // 读取DEM数据点List<Vector2>terrainPoints=LoadTerrainData();vartriangles=triangulator.Triangulate(terrainPoints);
  2. 有限元分析

    // 生成结构网格Meshmesh=newMesh();Vector3[]vertices=ConvertTo3D(triangles);int[]trianglesIndices=GetTrianglesIndices(triangles);mesh.vertices=vertices;mesh.triangles=trianglesIndices;

参考代码 基于C#实现的使用逐点插入法生成Delaunay三角网剖分程序www.youwenfan.com/contentcsr/112316.html

七、调试建议

  1. 可视化调试

    • 使用Unity Gizmos绘制外接圆和边

    • 添加日志输出关键计算步骤

  2. 异常处理

    try{// 三角剖分核心代码}catch(ArgumentExceptionex){Debug.LogError($"输入点集无效:{ex.Message}");}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 7:08:35

QT button

ToolButton 主要的属性 objectNmae : text&#xff1a; 名字 icon&#xff1a; 图片的显示&#xff08;这里需要添加source 添加文件->QTsoruce->添加图片文件&#xff09; toolTIp&#xff1a; 注释&#xff08;只有在鼠标放上去的时候会显示的提示内容&#xff09; …

作者头像 李华
网站建设 2026/5/1 9:51:37

[Web自动化] Selenium获取元素的子孙元素

10.10 Selenium获取元素的子孙元素 在Selenium中&#xff0c;获取某个元素的所有子孙元素可以通过几种不同的方法实现。以下是一些常见的方法&#xff1a; 10.10.1 使用 XPath XPath 是一种在HTML文档中查找信息的语言&#xff0c;非常适合在Selenium中使用。要获取某个元素的所…

作者头像 李华
网站建设 2026/5/1 7:20:25

人类最强的思维库:不是鸡汤,是能拿去用、能赚钱、能破局的那种

一、第一性原理思维&#xff08;所有强者的底层操作系统&#xff09; 一句话&#xff1a;把世界拆到不能再拆&#xff0c;然后从零开始重建。 问法模板&#xff1a; 这件事本质上是什么&#xff1f;哪些是物理/数学/人性不可违背的约束&#xff1f;哪些只是习惯、权威、共识、行…

作者头像 李华
网站建设 2026/5/1 7:12:02

我常用的爬虫利器,无脑采集Tiktok shop视频数据

爬虫为什么难&#xff1f; 爬虫是网络数据采集的简称&#xff0c;顾名思义就是利用http请求技术向网站发送数据请求&#xff0c;然后进行html解析并提取到需要的数据&#xff0c;可以使用Python等工具实现&#xff0c;这个过程看似简单&#xff0c;但暗藏很多机关&#xff0c;…

作者头像 李华
网站建设 2026/5/1 7:19:49

Java高频面试题:Java中变量和常量有什么区别?

大家好&#xff0c;我是锋哥。今天分享关于【Java高频面试题&#xff1a;Java中变量和常量有什么区别?】面试题。希望对大家有帮助&#xff1b;Java高频面试题&#xff1a;Java中变量和常量有什么区别?在Java中&#xff0c;变量和常量都是存储数据的手段&#xff0c;但它们在…

作者头像 李华
网站建设 2026/5/1 7:14:29

豆瓣电影大数据分析系统定制(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

豆瓣电影大数据分析系统定制(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_相关定制&#xff09;_文章底部可以扫码 可要求使用组件&#xff0c;代码分析为主&#xff0c;可加推荐算法&#xff0c;可视化组件按要求来&#xff0c;动态展示没问题。系统定制时间按需&…

作者头像 李华