如何从线创建 3D 平面
How to create 3D plane from lines
我正在将 Helix Toolkit 与 WPF 一起使用,我想将一堆线条变成一个表面。澄清一下,我有一组 3 维曲线,我想得到所有这些线产生的曲线 blade。 (线条代表通过 blade 的线条)。
在某种程度上,我想做与 this question 所问相反的事情(使用线作为线框将其变成模型)。
到目前为止我有这个 XAML:
<Window x:Class="_3D_Geometry_Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:helix="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
xmlns:local="clr-namespace:_3D_Geometry_Test">
<Grid>
<helix:HelixViewport3D x:Name="view1">
<helix:DefaultLights/>
<helix:MeshGeometryVisual3D x:Name="bladesMeshGeo" />
</helix:HelixViewport3D>
</Grid>
</Window>
和 code-behind 的相关部分(我不包括 GetSplines()
和 GetSpars()
的内容,因为它们完全由我添加很多 Point3D
objects 到每个列表):
using HelixToolkit.Wpf;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace _3D_Geometry_Test
{
public partial class MainWindow : Window
{
const int NUM_SPLINES = 11;
const int NUM_SPARS = 10;
public MainWindow()
{
InitializeComponent();
List<Point3D>[] splines = GetSplines();
List<Point3D>[] spars = GetSpars();
for (int i = 0; i < NUM_SPLINES; i++)
{
bladesMeshGeo.Children.Add(new LinesVisual3D
{
Points = new Point3DCollection(splines[i]),
Color = Colors.Red
});
}
for (int i = 0; i < NUM_SPARS; i++)
{
bladesMeshGeo.Children.Add(new LinesVisual3D
{
Points = new Point3DCollection(spars[i]),
Color = Colors.Blue
});
}
}
}
}
结果是这样的:
但我想要这样的东西:
编辑: 我不喜欢 Helix Toolkit,所以如果有人知道另一个可以完成此任务的库,我将不胜感激!
好的,所以我想出的最好办法是制作很多很多很多连接所有点的小三角形,就像这样(XAML 保持不变):
using HelixToolkit.Wpf;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace _3D_Geometry_Test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
const int NUM_SPLINES = 11;
const int NUM_SPARS = 10;
static Vector3D xAxis = new Vector3D(1, 0, 0);
/// <summary>
/// Creates the window.
/// </summary>
public MainWindow()
{
InitializeComponent();
MakeBlades3();
}
/// <summary>
/// Makes the blades.
/// </summary>
public void MakeBlades3()
{
var splines = GetSplines();
var spars = GetSpars();
MeshBuilder builder = new MeshBuilder(true, true);
for (int i = 0; i < splines.Length; i++)
{
var currSpline = splines[i];
if (i < splines.Length - 1)
{
var nextSpline = splines[i + 1];
for (int j = 0; j < currSpline.Count; j++)
{
Point3D currPoint = currSpline[j];
Point3D pt1, pt2;
Find2NN(currPoint, nextSpline, out pt1, out pt2);
builder.AddTriangle(currPoint, pt1, pt2);
if (j > 0)
{
Point3D prevPoint = currSpline[j - 1];
Point3D pt3 = FindNN(currPoint, prevPoint, nextSpline);
builder.AddTriangle(currPoint, prevPoint, pt3);
}
if (j < currSpline.Count - 1)
{
Point3D nextPoint = currSpline[j + 1];
Point3D pt3 = FindNN(currPoint, nextPoint, nextSpline);
builder.AddTriangle(currPoint, nextPoint, pt3);
}
}
}
if (i > 0)
{
var prevSpline = splines[i - 1];
for (int j = 0; j < currSpline.Count; j++)
{
Point3D currPoint = currSpline[j];
Point3D pt1, pt2;
Find2NN(currPoint, prevSpline, out pt1, out pt2);
builder.AddTriangle(currPoint, pt1, pt2);
if (j > 0)
{
Point3D prevPoint = currSpline[j - 1];
Point3D pt3 = FindNN(currPoint, prevPoint, prevSpline);
builder.AddTriangle(currPoint, prevPoint, pt3);
}
if (j < currSpline.Count - 1)
{
Point3D nextPoint = currSpline[j + 1];
Point3D pt3 = FindNN(currPoint, nextPoint, prevSpline);
builder.AddTriangle(currPoint, nextPoint, pt3);
}
}
}
}
bladePlot.MeshGeometry = builder.ToMesh();
}
/// <summary>
/// Finds the point in the input list of points that is closest to the two input points (Euclidean distance).
/// </summary>
/// <param name="origPoint1">The first point.</param>
/// <param name="origPoint2">The second point.</param>
/// <param name="listOfPoints">The list of points to find the nearest one in <note type="note">For the sake of speed, this should be a DISTINCT list (no duplicates).</note>.</param>
/// <returns>The point in <paramref name="listOfPoints"/> that is closest to <paramref name="origPoint1"/> and <paramref name="origPoint2"/> (Euclidean distance).</returns>
private Point3D FindNN(Point3D origPoint1, Point3D origPoint2, List<Point3D> listOfPoints)
{
Point3D result = listOfPoints[0];
var dist = EuclidDist(origPoint1, result) + EuclidDist(origPoint2, result);
for (int i = 1; i < listOfPoints.Count;i++)
{
var dist2 = EuclidDist(origPoint1, listOfPoints[i]) + EuclidDist(origPoint2, listOfPoints[i]);
if (dist2 < dist)
{
dist = dist2;
result = listOfPoints[i];
}
}
return result;
}
/// <summary>
/// Find the 2 nearest neighbors of the specified point (based on Euclidean distance).
/// </summary>
/// <param name="origPoint">The original point.</param>
/// <param name="listOfPoints">A list of points to find the two nearest-neighbors from. <note type="note">For the sake of speed, this should be a DISTINCT list (no duplicates).</note></param>
/// <param name="pt1">First nearest neighboring point (output).</param>
/// <param name="pt2">Second nearest neighboring point (output).</param>
private void Find2NN(Point3D origPoint, List<Point3D> listOfPoints, out Point3D pt1, out Point3D pt2)
{
pt1 = new Point3D();
pt2 = new Point3D();
List<Point3D> temp = new List<Point3D>(listOfPoints);
pt1 = temp[0];
var dist = EuclidDist(origPoint, pt1);
for (int i = 1; i < temp.Count; i++)
{
var dist2 = EuclidDist(origPoint, temp[i]);
if (dist2 < dist)
{
dist = dist2;
pt1 = temp[i];
}
}
temp.Remove(pt1);
pt2 = temp[0];
dist = EuclidDist(origPoint, pt2);
for (int i = 1; i < temp.Count; i++)
{
var dist2 = EuclidDist(origPoint, temp[i]);
if (dist2 < dist)
{
dist = dist2;
pt2 = temp[i];
}
}
}
/// <summary>
/// Calculates the Euclidean distance between the two input points.
/// </summary>
/// <param name="pt1">The first point.</param>
/// <param name="pt2">The second point.</param>
/// <returns>The Euclidean distance between <paramref name="pt1"/> and <paramref name="pt2"/>.</returns>
private double EuclidDist(Point3D pt1, Point3D pt2)
{
double deltaX = pt1.X - pt2.X;
double deltaY = pt1.Y - pt2.Y;
double deltaZ = pt1.Z - pt2.Z;
return Math.Sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
}
}
}
生成的图像如下所示:
这绝对是对线框的改进,但它仍然不是很流畅(不确定它是否显示在我发布的图片上),我相信必须有更好的解决方案,所以我'暂时不回答这个问题。只是发布我的(临时)解决方案以防它对任何人有帮助。
我正在将 Helix Toolkit 与 WPF 一起使用,我想将一堆线条变成一个表面。澄清一下,我有一组 3 维曲线,我想得到所有这些线产生的曲线 blade。 (线条代表通过 blade 的线条)。
在某种程度上,我想做与 this question 所问相反的事情(使用线作为线框将其变成模型)。
到目前为止我有这个 XAML:
<Window x:Class="_3D_Geometry_Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:helix="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
xmlns:local="clr-namespace:_3D_Geometry_Test">
<Grid>
<helix:HelixViewport3D x:Name="view1">
<helix:DefaultLights/>
<helix:MeshGeometryVisual3D x:Name="bladesMeshGeo" />
</helix:HelixViewport3D>
</Grid>
</Window>
和 code-behind 的相关部分(我不包括 GetSplines()
和 GetSpars()
的内容,因为它们完全由我添加很多 Point3D
objects 到每个列表):
using HelixToolkit.Wpf;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace _3D_Geometry_Test
{
public partial class MainWindow : Window
{
const int NUM_SPLINES = 11;
const int NUM_SPARS = 10;
public MainWindow()
{
InitializeComponent();
List<Point3D>[] splines = GetSplines();
List<Point3D>[] spars = GetSpars();
for (int i = 0; i < NUM_SPLINES; i++)
{
bladesMeshGeo.Children.Add(new LinesVisual3D
{
Points = new Point3DCollection(splines[i]),
Color = Colors.Red
});
}
for (int i = 0; i < NUM_SPARS; i++)
{
bladesMeshGeo.Children.Add(new LinesVisual3D
{
Points = new Point3DCollection(spars[i]),
Color = Colors.Blue
});
}
}
}
}
结果是这样的:
但我想要这样的东西:
编辑: 我不喜欢 Helix Toolkit,所以如果有人知道另一个可以完成此任务的库,我将不胜感激!
好的,所以我想出的最好办法是制作很多很多很多连接所有点的小三角形,就像这样(XAML 保持不变):
using HelixToolkit.Wpf;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace _3D_Geometry_Test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
const int NUM_SPLINES = 11;
const int NUM_SPARS = 10;
static Vector3D xAxis = new Vector3D(1, 0, 0);
/// <summary>
/// Creates the window.
/// </summary>
public MainWindow()
{
InitializeComponent();
MakeBlades3();
}
/// <summary>
/// Makes the blades.
/// </summary>
public void MakeBlades3()
{
var splines = GetSplines();
var spars = GetSpars();
MeshBuilder builder = new MeshBuilder(true, true);
for (int i = 0; i < splines.Length; i++)
{
var currSpline = splines[i];
if (i < splines.Length - 1)
{
var nextSpline = splines[i + 1];
for (int j = 0; j < currSpline.Count; j++)
{
Point3D currPoint = currSpline[j];
Point3D pt1, pt2;
Find2NN(currPoint, nextSpline, out pt1, out pt2);
builder.AddTriangle(currPoint, pt1, pt2);
if (j > 0)
{
Point3D prevPoint = currSpline[j - 1];
Point3D pt3 = FindNN(currPoint, prevPoint, nextSpline);
builder.AddTriangle(currPoint, prevPoint, pt3);
}
if (j < currSpline.Count - 1)
{
Point3D nextPoint = currSpline[j + 1];
Point3D pt3 = FindNN(currPoint, nextPoint, nextSpline);
builder.AddTriangle(currPoint, nextPoint, pt3);
}
}
}
if (i > 0)
{
var prevSpline = splines[i - 1];
for (int j = 0; j < currSpline.Count; j++)
{
Point3D currPoint = currSpline[j];
Point3D pt1, pt2;
Find2NN(currPoint, prevSpline, out pt1, out pt2);
builder.AddTriangle(currPoint, pt1, pt2);
if (j > 0)
{
Point3D prevPoint = currSpline[j - 1];
Point3D pt3 = FindNN(currPoint, prevPoint, prevSpline);
builder.AddTriangle(currPoint, prevPoint, pt3);
}
if (j < currSpline.Count - 1)
{
Point3D nextPoint = currSpline[j + 1];
Point3D pt3 = FindNN(currPoint, nextPoint, prevSpline);
builder.AddTriangle(currPoint, nextPoint, pt3);
}
}
}
}
bladePlot.MeshGeometry = builder.ToMesh();
}
/// <summary>
/// Finds the point in the input list of points that is closest to the two input points (Euclidean distance).
/// </summary>
/// <param name="origPoint1">The first point.</param>
/// <param name="origPoint2">The second point.</param>
/// <param name="listOfPoints">The list of points to find the nearest one in <note type="note">For the sake of speed, this should be a DISTINCT list (no duplicates).</note>.</param>
/// <returns>The point in <paramref name="listOfPoints"/> that is closest to <paramref name="origPoint1"/> and <paramref name="origPoint2"/> (Euclidean distance).</returns>
private Point3D FindNN(Point3D origPoint1, Point3D origPoint2, List<Point3D> listOfPoints)
{
Point3D result = listOfPoints[0];
var dist = EuclidDist(origPoint1, result) + EuclidDist(origPoint2, result);
for (int i = 1; i < listOfPoints.Count;i++)
{
var dist2 = EuclidDist(origPoint1, listOfPoints[i]) + EuclidDist(origPoint2, listOfPoints[i]);
if (dist2 < dist)
{
dist = dist2;
result = listOfPoints[i];
}
}
return result;
}
/// <summary>
/// Find the 2 nearest neighbors of the specified point (based on Euclidean distance).
/// </summary>
/// <param name="origPoint">The original point.</param>
/// <param name="listOfPoints">A list of points to find the two nearest-neighbors from. <note type="note">For the sake of speed, this should be a DISTINCT list (no duplicates).</note></param>
/// <param name="pt1">First nearest neighboring point (output).</param>
/// <param name="pt2">Second nearest neighboring point (output).</param>
private void Find2NN(Point3D origPoint, List<Point3D> listOfPoints, out Point3D pt1, out Point3D pt2)
{
pt1 = new Point3D();
pt2 = new Point3D();
List<Point3D> temp = new List<Point3D>(listOfPoints);
pt1 = temp[0];
var dist = EuclidDist(origPoint, pt1);
for (int i = 1; i < temp.Count; i++)
{
var dist2 = EuclidDist(origPoint, temp[i]);
if (dist2 < dist)
{
dist = dist2;
pt1 = temp[i];
}
}
temp.Remove(pt1);
pt2 = temp[0];
dist = EuclidDist(origPoint, pt2);
for (int i = 1; i < temp.Count; i++)
{
var dist2 = EuclidDist(origPoint, temp[i]);
if (dist2 < dist)
{
dist = dist2;
pt2 = temp[i];
}
}
}
/// <summary>
/// Calculates the Euclidean distance between the two input points.
/// </summary>
/// <param name="pt1">The first point.</param>
/// <param name="pt2">The second point.</param>
/// <returns>The Euclidean distance between <paramref name="pt1"/> and <paramref name="pt2"/>.</returns>
private double EuclidDist(Point3D pt1, Point3D pt2)
{
double deltaX = pt1.X - pt2.X;
double deltaY = pt1.Y - pt2.Y;
double deltaZ = pt1.Z - pt2.Z;
return Math.Sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
}
}
}
生成的图像如下所示:
这绝对是对线框的改进,但它仍然不是很流畅(不确定它是否显示在我发布的图片上),我相信必须有更好的解决方案,所以我'暂时不回答这个问题。只是发布我的(临时)解决方案以防它对任何人有帮助。