明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 776|回复: 3

[资源] 三角网内插高程点

[复制链接]
发表于 2025-2-20 15:44:14 | 显示全部楼层 |阅读模式
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry

Module TriangleElevation

    '三角形内插入高程点
    Sub Main()
        Dim docLock As DocumentLock = Core.Application.DocumentManager.MdiActiveDocument.LockDocument() '“非模态窗口,要锁定文档”
        NativeMethods.SetFocus(Core.Application.DocumentManager.MdiActiveDocument.Window.Handle) 'CAD获得焦点

        ' 获取当前文档和数据库
        Dim doc As Document = Core.Application.DocumentManager.MdiActiveDocument
        Dim db As Database = doc.Database
        Dim ed As Editor = doc.Editor

        ' 提示用户选择三角形(假设三角形是由 3D 面或多段线表示)
        Dim peo As New PromptSelectionOptions()
        peo.MessageForAdding = "请选择三角形(由 3D 面或多段线表示): "
        Dim psr As PromptSelectionResult = ed.GetSelection(peo)

        ' 检查选择结果是否有效
        If psr.Status <> PromptStatus.OK Then
            ed.WriteMessage("未选择任何三角形对象。")
            Return
        End If

        Do
            ' 遍历选择集中的每个三角形对象
            Using tr As Transaction = db.TransactionManager.StartTransaction()
                ' 提示用户在三角形内拾取一点
                Dim ppr As PromptPointResult = ed.GetPoint("请在三角形内拾取一点: ")

                ' 检查拾取点结果是否有效
                If ppr.Status <> PromptStatus.OK Then
                    ed.WriteMessage("未拾取任何点。")
                    Exit Do
                Else
                    For Each id As ObjectId In psr.Value.GetObjectIds()
                        Dim ent As Entity = tr.GetObject(id, OpenMode.ForRead)
                        ' 检查对象是否为 Polyline 或 Polyline3d
                        If TypeOf ent Is Polyline OrElse TypeOf ent Is Polyline3d Then
                            ' 获取三角形顶点
                            Dim vertices As Point3dCollection = GetTriangleVertices(ent, tr)
                            ' 检查拾取的点是否在三角形内
                            Dim pickedPoint As Point3d = ppr.Value
                            If IsPointInTriangle(vertices, pickedPoint) Then

                                ' 计算拾取点的高程值
                                Dim elevation As Double = CalculateElevation(vertices, pickedPoint)

                                ' 在图上生成点和文本
                                CreatePointAndText(db, tr, pickedPoint, elevation)

                                ' 刷新图形视图以显示新创建的点和文本
                                ed.UpdateScreen()

                                Exit For
                            Else
                                ed.WriteMessage("拾取的点不在三角形内。")
                            End If
                        End If
                    Next

                End If

                tr.Commit()
            End Using
        Loop

        docLock.Dispose()'解锁文档
    End Sub

    ' 获取三角形的顶点
    Function GetTriangleVertices(ent As Entity, tr As Transaction) As Point3dCollection
        Dim vertices As New Point3dCollection()

        If TypeOf ent Is Polyline Then
            Dim pline As Polyline = CType(ent, Polyline)
            For i As Integer = 0 To pline.NumberOfVertices - 1
                vertices.Add(pline.GetPoint3dAt(i))
            Next
        ElseIf TypeOf ent Is Polyline3d Then
            Dim pline3d As Polyline3d = CType(ent, Polyline3d)
            For Each vId As ObjectId In pline3d
                Dim v As PolylineVertex3d = CType(tr.GetObject(vId, OpenMode.ForRead), PolylineVertex3d)
                vertices.Add(v.Position)
            Next
        End If

        Return vertices
    End Function

    ' 计算拾取点的高程值
    Function CalculateElevation(vertices As Point3dCollection, pickedPoint As Point3d) As Double
        ' 确保三角形是平面的
        If vertices.Count <> 3 Then Return 0.0

        ' 使用重心坐标法计算高程值
        Dim x1 As Double = vertices(0).X
        Dim y1 As Double = vertices(0).Y
        Dim z1 As Double = vertices(0).Z

        Dim x2 As Double = vertices(1).X
        Dim y2 As Double = vertices(1).Y
        Dim z2 As Double = vertices(1).Z

        Dim x3 As Double = vertices(2).X
        Dim y3 As Double = vertices(2).Y
        Dim z3 As Double = vertices(2).Z

        Dim x As Double = pickedPoint.X
        Dim y As Double = pickedPoint.Y

        ' 计算重心坐标
        Dim detT As Double = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)
        Dim lambda1 As Double = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / detT
        Dim lambda2 As Double = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / detT
        Dim lambda3 As Double = 1.0 - lambda1 - lambda2

        ' 计算插值高程
        Dim elevation As Double = lambda1 * z1 + lambda2 * z2 + lambda3 * z3

        Return elevation
    End Function

    ' 判断点是否在三角形内
    Function IsPointInTriangle(vertices As Point3dCollection, point As Point3d) As Boolean
        Dim x1 As Double = vertices(0).X
        Dim y1 As Double = vertices(0).Y

        Dim x2 As Double = vertices(1).X
        Dim y2 As Double = vertices(1).Y

        Dim x3 As Double = vertices(2).X
        Dim y3 As Double = vertices(2).Y

        Dim x As Double = point.X
        Dim y As Double = point.Y

        Dim denominator As Double = ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3))
        Dim a As Double = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / denominator
        Dim b As Double = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / denominator
        Dim c As Double = 1 - a - b

        ' 检查点是否在三角形内
        Return 0 <= a AndAlso a <= 1 AndAlso 0 <= b AndAlso b <= 1 AndAlso 0 <= c AndAlso c <= 1
    End Function

    ' 在图上生成点和文本
    Sub CreatePointAndText(db As Database, tr As Transaction, point As Point3d, elevation As Double)
        Dim bt As BlockTable = CType(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable)
        Dim btr As BlockTableRecord = CType(tr.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)

        ' 创建点对象
        Dim dbPoint As New DBPoint(point) With {
            .ColorIndex = 1 ' 红色
            }
        AppendEntity(dbPoint)

        ' 创建文本对象
        Dim text As New DBText With {
            .Position = New Point3d(point.X, point.Y, point.Z + 0.5), ' 将文本位置稍微偏高
            .TextString = elevation.ToString("F2"),
            .Height = 1.0,
            .ColorIndex = 2 ' 黄色
            }
        AppendEntity(text)

    End Sub

End Module
回复

使用道具 举报

发表于 2025-2-22 00:58:23 | 显示全部楼层
应该加上四叉树,不然会很影响速度,就没啥实际意义了
回复 支持 反对

使用道具 举报

发表于 2025-2-22 22:45:34 | 显示全部楼层
判断点是否在三角形内的函数应加上容差判断,当点落于三角形边上时,a、b、c三个变量中会有1个或2个非常接近0,此时该函数有时会判断在外,有时又判断在内,故调整为如下,其中的容差xEps取值为10的负10次方。
  1.         ' 判断点在三角形内
  2.         Public Function IsPointInTriangle(ByVal vertices As Point3dCollection, ByVal point As Point3d, ByVal xEps As Double) As Boolean
  3.             Dim x1 As Double = vertices(0).X
  4.             Dim y1 As Double = vertices(0).Y

  5.             Dim x2 As Double = vertices(1).X
  6.             Dim y2 As Double = vertices(1).Y

  7.             Dim x3 As Double = vertices(2).X
  8.             Dim y3 As Double = vertices(2).Y

  9.             Dim x As Double = point.X
  10.             Dim y As Double = point.Y

  11.             Dim denominator As Double = ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3))
  12.             Dim a As Double = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / denominator
  13.             Dim b As Double = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / denominator
  14.             Dim c As Double = 1 - a - b

  15.             If Math.Abs(a) <= xEps Then a = 0.0
  16.             If Math.Abs(b) <= xEps Then b = 0.0
  17.             If Math.Abs(c) <= xEps Then c = 0.0

  18.             ' 检查点是否在三角形内
  19.             Return 0 <= a AndAlso a <= 1 AndAlso 0 <= b AndAlso b <= 1 AndAlso 0 <= c AndAlso c <= 1
  20.         End Function
回复 支持 反对

使用道具 举报

发表于 2025-2-24 10:48:39 | 显示全部楼层
本帖最后由 czb203 于 2025-2-24 10:50 编辑
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;

  4. public struct Point
  5. {
  6.     public double X { get; }
  7.     public double Y { get; }
  8.     public double Z { get; }

  9.     public Point(double x, double y, double z)
  10.     {
  11.         X = x;
  12.         Y = y;
  13.         Z = z;
  14.     }
  15. }

  16. public class Triangle
  17. {
  18.     public Point P1 { get; }
  19.     public Point P2 { get; }
  20.     public Point P3 { get; }
  21.     public double MinX { get; }
  22.     public double MaxX { get; }
  23.     public double MinY { get; }
  24.     public double MaxY { get; }

  25.     public Triangle(Point p1, Point p2, Point p3)
  26.     {
  27.         P1 = p1;
  28.         P2 = p2;
  29.         P3 = p3;
  30.         MinX = Math.Min(p1.X, Math.Min(p2.X, p3.X));
  31.         MaxX = Math.Max(p1.X, Math.Max(p2.X, p3.X));
  32.         MinY = Math.Min(p1.Y, Math.Min(p2.Y, p3.Y));
  33.         MaxY = Math.Max(p1.Y, Math.Max(p2.Y, p3.Y));
  34.     }
  35. }

  36. public class QuadTree
  37. {
  38.     private const int MaxCapacity = 4;
  39.     private readonly List<Triangle> triangles = new List<Triangle>();
  40.     private QuadTree[] children;
  41.     private readonly double xMin, yMin, xMax, yMax;

  42.     public QuadTree(double xMin, double yMin, double xMax, double yMax)
  43.     {
  44.         this.xMin = xMin;
  45.         this.yMin = yMin;
  46.         this.xMax = xMax;
  47.         this.yMax = yMax;
  48.     }

  49.     public void Insert(Triangle triangle)
  50.     {
  51.         if (children != null)
  52.         {
  53.             foreach (var child in children)
  54.             {
  55.                 if (ChildIntersectsTriangle(child, triangle))
  56.                 {
  57.                     child.Insert(triangle);
  58.                 }
  59.             }
  60.             return;
  61.         }

  62.         triangles.Add(triangle);

  63.         if (triangles.Count > MaxCapacity)
  64.         {
  65.             Split();
  66.             foreach (var t in triangles)
  67.             {
  68.                 foreach (var child in children)
  69.                 {
  70.                     if (ChildIntersectsTriangle(child, t))
  71.                     {
  72.                         child.Insert(t);
  73.                     }
  74.                 }
  75.             }
  76.             triangles.Clear();
  77.         }
  78.     }

  79.     private bool ChildIntersectsTriangle(QuadTree child, Triangle triangle)
  80.     {
  81.         return !(triangle.MinX > child.xMax ||
  82.                  triangle.MaxX < child.xMin ||
  83.                  triangle.MinY > child.yMax ||
  84.                  triangle.MaxY < child.yMin);
  85.     }

  86.     private void Split()
  87.     {
  88.         double xMid = (xMin + xMax) / 2;
  89.         double yMid = (yMin + yMax) / 2;

  90.         children = new QuadTree[4];
  91.         children[0] = new QuadTree(xMin, yMin, xMid, yMid);
  92.         children[1] = new QuadTree(xMid, yMin, xMax, yMid);
  93.         children[2] = new QuadTree(xMin, yMid, xMid, yMax);
  94.         children[3] = new QuadTree(xMid, yMid, xMax, yMax);
  95.     }

  96.     public List<Triangle> Query(double x, double y)
  97.     {
  98.         var result = new List<Triangle>();
  99.         if (x < xMin || x > xMax || y < yMin || y > yMax)
  100.             return result;

  101.         if (children != null)
  102.         {
  103.             foreach (var child in children)
  104.             {
  105.                 result.AddRange(child.Query(x, y));
  106.             }
  107.         }
  108.         else
  109.         {
  110.             result.AddRange(triangles);
  111.         }

  112.         return result;
  113.     }
  114. }

  115. public static class TriangleHelper
  116. {
  117.     public static bool IsPointInTriangle(Point p, Triangle triangle, double epsilon = 1e-6)
  118.     {
  119.         double denominator = ((triangle.P2.Y - triangle.P3.Y) * (triangle.P1.X - triangle.P3.X)) +
  120.                             ((triangle.P3.X - triangle.P2.X) * (triangle.P1.Y - triangle.P3.Y));

  121.         if (Math.Abs(denominator) < epsilon)
  122.             return false;

  123.         double a = ((triangle.P2.Y - triangle.P3.Y) * (p.X - triangle.P3.X) +
  124.                    (triangle.P3.X - triangle.P2.X) * (p.Y - triangle.P3.Y)) / denominator;
  125.         double b = ((triangle.P3.Y - triangle.P1.Y) * (p.X - triangle.P3.X) +
  126.                    (triangle.P1.X - triangle.P3.X) * (p.Y - triangle.P3.Y)) / denominator;
  127.         double c = 1 - a - b;

  128.         return a >= -epsilon && a <= 1 + epsilon &&
  129.                b >= -epsilon && b <= 1 + epsilon &&
  130.                c >= -epsilon && c <= 1 + epsilon &&
  131.                Math.Abs(a + b + c - 1) < 3 * epsilon;
  132.     }

  133.     public static double InterpolateZ(Point p, Triangle triangle)
  134.     {
  135.         double denominator = ((triangle.P2.Y - triangle.P3.Y) * (triangle.P1.X - triangle.P3.X)) +
  136.                              ((triangle.P3.X - triangle.P2.X) * (triangle.P1.Y - triangle.P3.Y));

  137.         double a = ((triangle.P2.Y - triangle.P3.Y) * (p.X - triangle.P3.X) +
  138.                    (triangle.P3.X - triangle.P2.X) * (p.Y - triangle.P3.Y)) / denominator;
  139.         double b = ((triangle.P3.Y - triangle.P1.Y) * (p.X - triangle.P3.X) +
  140.                    (triangle.P1.X - triangle.P3.X) * (p.Y - triangle.P3.Y)) / denominator;
  141.         double c = 1 - a - b;

  142.         return a * triangle.P1.Z + b * triangle.P2.Z + c * triangle.P3.Z;
  143.     }
  144. }

  145. public class TerrainInterpolator
  146. {
  147.     private readonly QuadTree quadTree;

  148.     public TerrainInterpolator(IEnumerable<Triangle> triangles)
  149.     {
  150.         var triangleList = triangles.ToList();
  151.         double xMin = triangleList.Min(t => t.MinX);
  152.         double xMax = triangleList.Max(t => t.MaxX);
  153.         double yMin = triangleList.Min(t => t.MinY);
  154.         double yMax = triangleList.Max(t => t.MaxY);

  155.         quadTree = new QuadTree(xMin, yMin, xMax, yMax);
  156.         foreach (var triangle in triangleList)
  157.         {
  158.             quadTree.Insert(triangle);
  159.         }
  160.     }

  161.     public double? Interpolate(Point p)
  162.     {
  163.         var candidates = quadTree.Query(p.X, p.Y);
  164.         foreach (var triangle in candidates)
  165.         {
  166.             if (TriangleHelper.IsPointInTriangle(p, triangle))
  167.             {
  168.                 return TriangleHelper.InterpolateZ(p, triangle);
  169.             }
  170.         }
  171.         return null;
  172.     }
  173. }

  174. // 使用示例
  175. class Program
  176. {
  177.     static void Main()
  178.     {
  179.         // 创建测试三角形
  180.         var triangles = new List<Triangle>
  181.         {
  182.             new Triangle(
  183.                 new Point(0, 0, 100),
  184.                 new Point(10, 0, 200),
  185.                 new Point(5, 10, 300)
  186.             )
  187.             // 添加更多三角形...
  188.         };

  189.         var interpolator = new TerrainInterpolator(triangles);
  190.         
  191.         Point testPoint = new Point(5, 5, 0);
  192.         double? elevation = interpolator.Interpolate(testPoint);

  193.         if (elevation.HasValue)
  194.         {
  195.             Console.WriteLine($"插值高程值: {elevation.Value}");
  196.         }
  197.         else
  198.         {
  199.             Console.WriteLine("点不在任何三角形内");
  200.         }
  201.     }
  202. }
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2025-3-30 01:05 , Processed in 0.221118 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表