Как создать 3D-плоскость из линий

Я использую Helix Toolkit с WPF и хочу взять несколько линий и превратить их в поверхность. Чтобы уточнить, у меня есть группа трехмерных кривых, и я хочу получить изогнутое лезвие, которое получится из всех этих линий. (Линии представляют собой линии, проходящие через лезвие).

В какой-то степени я хочу сделать противоположное тому, что задавал этот вопрос о (используйте линии как проволочный каркас, чтобы превратить его в модель).

Пока у меня есть этот 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>

И соответствующая часть кода программной части (я не включаю содержимое GetSplines() и GetSpars(), так как они полностью состоят из того, что я добавляю множество объектов Point3D в каждый список):

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, поэтому, если кто-нибудь знает о другой библиотеке, которая может это сделать, я был бы благодарен услышать об этом!


person Eliza Bennet    schedule 11.01.2017    source источник
comment
Возможно, поверхностная демонстрация может вам помочь. В зависимости от того, как вы создаете сплайны: github. com/helix-toolkit/helix-toolkit/tree/master/Source/   -  person egse    schedule 16.01.2017
comment
@egse Я смотрел на это, но я не мог понять, как рисуется реальная поверхность - я даже не мог понять, какой класс отвечает за создание поверхности.   -  person Eliza Bennet    schedule 17.01.2017
comment
О, и сплайны в настоящее время «генерируются» коллегой, который дает мне несколько списков 3D-точек. Я все еще жду фактической математики, которая произвела эти точки, поэтому прямо сейчас я пытаюсь перейти от списка 3D-точек к 3D-кривой и 3D-поверхности, не зная механизма, который создает 3D-точки (что может может быть частью проблемы).   -  person Eliza Bennet    schedule 17.01.2017


Ответы (1)


Итак, лучшее, что я придумал, включает в себя создание множества, множества и множества маленьких треугольников, соединяющих все точки, например так (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);
        }
    }
}

Полученное изображение выглядит следующим образом:

Изображение

Это определенно улучшение по сравнению с каркасом, но оно все еще не очень гладкое (не уверен, что это видно на моем опубликованном изображении), и я убежден, что должно быть лучшее решение, поэтому я оставлю это без ответа. на данный момент. Просто публикую свое (временное) решение на случай, если оно кому-нибудь поможет.

person Eliza Bennet    schedule 13.01.2017
comment
Я отмечаю этот ответ как правильный, потому что это решение, которое я буду использовать. - person Eliza Bennet; 18.01.2017