3D-точки динамического выполнения в WPF

#wpf #3d #point-clouds

Вопрос:

Я пытаюсь создать динамическое представление 3D-точек с помощью WPF, я могу нарисовать первый набор точек, но затем он выходит из строя, потому что Mesh3DGeometry заморожена .. есть ли способ разморозить Mesh3DGeomerty и очистить сетку, чтобы можно было нарисовать следующие наборы ?

     var rand = new Random();
while (true)
{
    points.Clear();
    for (int i = 0; i < 100; i  )
    {
        points.Add(new Point3D(rand.NextDouble(), rand.NextDouble(), rand.NextDouble()));
    }
    for (int i = 0; i < points.Count; i  )
    {
        AddCubeToMesh(pointCloudMesh, points[i], 0.01);
    }
    pointCloudMesh.Freeze();
}
 

Также здесь есть XAML

 <Grid Grid.Row="1" Grid.Column="1"  Margin="0 20 0 -20" Background="LightGray" Name="MyCanvas" >
                <Viewport3D>
                    <Viewport3D.Camera>
                        <PerspectiveCamera x:Name="camMain" Position="0 0 5" LookDirection="0 0 -5"/>
                    </Viewport3D.Camera>

                    <ModelVisual3D>
                        <ModelVisual3D.Content>
                            <Model3DGroup>
                                <Model3DGroup.Children>
                                    <AmbientLight Color="#AAAAAA" />
                                </Model3DGroup.Children>
                            </Model3DGroup>
                        </ModelVisual3D.Content>
                    </ModelVisual3D>

                    <ModelVisual3D>
                        <ModelVisual3D.Content>
                            <GeometryModel3D>
                                <GeometryModel3D.Geometry>
                                    <MeshGeometry3D x:Name="pointCloudMesh" />
                                </GeometryModel3D.Geometry>
                                <GeometryModel3D.Material>
                                    <DiffuseMaterial Brush="Red"/>
                                </GeometryModel3D.Material>
                            </GeometryModel3D>
                        </ModelVisual3D.Content>
                    </ModelVisual3D>
                </Viewport3D>
            </Grid>
 

Код для AddCubeToMesh

 private void AddCubeToMesh(MeshGeometry3D mesh, Point3D center, double size)
    {
        if (mesh != null)
        {
            int offset = mesh.Positions.Count;

            mesh.Positions.Add(new Point3D(center.X - size, center.Y   size, center.Z - size));
            mesh.Positions.Add(new Point3D(center.X   size, center.Y   size, center.Z - size));
            mesh.Positions.Add(new Point3D(center.X   size, center.Y   size, center.Z   size));
            mesh.Positions.Add(new Point3D(center.X - size, center.Y   size, center.Z   size));
            mesh.Positions.Add(new Point3D(center.X - size, center.Y - size, center.Z - size));
            mesh.Positions.Add(new Point3D(center.X   size, center.Y - size, center.Z - size));
            mesh.Positions.Add(new Point3D(center.X   size, center.Y - size, center.Z   size));
            mesh.Positions.Add(new Point3D(center.X - size, center.Y - size, center.Z   size));

            mesh.TriangleIndices.Add(offset   3);
            mesh.TriangleIndices.Add(offset   2);
            mesh.TriangleIndices.Add(offset   6);

            mesh.TriangleIndices.Add(offset   3);
            mesh.TriangleIndices.Add(offset   6);
            mesh.TriangleIndices.Add(offset   7);

            mesh.TriangleIndices.Add(offset   2);
            mesh.TriangleIndices.Add(offset   1);
            mesh.TriangleIndices.Add(offset   5);

            mesh.TriangleIndices.Add(offset   2);
            mesh.TriangleIndices.Add(offset   5);
            mesh.TriangleIndices.Add(offset   6);

            mesh.TriangleIndices.Add(offset   1);
            mesh.TriangleIndices.Add(offset   0);
            mesh.TriangleIndices.Add(offset   4);

            mesh.TriangleIndices.Add(offset   1);
            mesh.TriangleIndices.Add(offset   4);
            mesh.TriangleIndices.Add(offset   5);

            mesh.TriangleIndices.Add(offset   0);
            mesh.TriangleIndices.Add(offset   3);
            mesh.TriangleIndices.Add(offset   7);

            mesh.TriangleIndices.Add(offset   0);
            mesh.TriangleIndices.Add(offset   7);
            mesh.TriangleIndices.Add(offset   4);

            mesh.TriangleIndices.Add(offset   7);
            mesh.TriangleIndices.Add(offset   6);
            mesh.TriangleIndices.Add(offset   5);

            mesh.TriangleIndices.Add(offset   7);
            mesh.TriangleIndices.Add(offset   5);
            mesh.TriangleIndices.Add(offset   4);

            mesh.TriangleIndices.Add(offset   2);
            mesh.TriangleIndices.Add(offset   3);
            mesh.TriangleIndices.Add(offset   0);

            mesh.TriangleIndices.Add(offset   2);
            mesh.TriangleIndices.Add(offset   0);
            mesh.TriangleIndices.Add(offset   1);
        }
    }
 

Мое решение, которое вроде как сработало для меня, состоит в том, чтобы добавить все это в отдельный пользовательский элемент управления и вызывать его каждый раз, когда поступают новые данные

Комментарии:

1. Я бы добавил реализацию метода AddCubeToMesh к этому вопросу.

Ответ №1:

Во-первых: Бесконечный цикл, который генерирует части пользовательского интерфейса, не давая времени потоку пользовательского интерфейса, не будет работать. Создайте данные в отдельном потоке и передайте данные в поток пользовательского интерфейса, чтобы создать модель и отобразить ее.

Затем размораживание замораживаемого устройства невозможно:

«Размораживание» Замораживаемый объект После замораживания замораживаемый объект никогда не может быть изменен или разморожен; однако вы можете создать размороженный клон с помощью метода Clone или CloneCurrentValue.

Источник

Поэтому, если вы считаете, что действительно нужно заморозить/разморозить, вы можете сделать это, сделав клон. Я не думаю, что это нужно здесь.

Комментарии:

1. Таким образом, бесконечный цикл предназначен только для демонстрации, а не для реального проекта. потому что данные поступают с аппаратного датчика .. однако я решил эту проблему, разделив 3D-представление на другой пользовательский элемент управления, а затем я могу удалить всех детей и создать их заново с новыми данными

2. Я думаю, что гораздо быстрее добавить 100 кубов в начало координат и обновлять их переводы при каждом тике.

Ответ №2:

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

XAML для нового пользовательского элемента управления

 <UserControl x:Class="LPDTool.View3D"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:LPDTool"
         mc:Ignorable="d" 
         d:DesignHeight="500" d:DesignWidth="500"
         KeyDown="Window_KeyDown">
<GroupBox x:Name="GBox" Margin="0 20 0 -20" Foreground="#AAAAAA" FontSize="18" FontWeight="Medium" Background="#DDDBDA" BorderBrush="#7B7704" BorderThickness="2" FontFamily="Abadi MT">
    <Viewport3D>
        <Viewport3D.Camera>
            <PerspectiveCamera x:Name="camMain"/>
        </Viewport3D.Camera>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <Model3DGroup>
                    <Model3DGroup.Children>
                        <AmbientLight Color="#AAAAAA" />
                    </Model3DGroup.Children>
                </Model3DGroup>
            </ModelVisual3D.Content>
        </ModelVisual3D>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <GeometryModel3D>
                    <GeometryModel3D.Geometry>
                        <MeshGeometry3D x:Name="pointCloudMesh" />
                    </GeometryModel3D.Geometry>
                    <GeometryModel3D.Material>
                        <DiffuseMaterial Brush="Red"/>
                    </GeometryModel3D.Material>
                </GeometryModel3D>
            </ModelVisual3D.Content>
        </ModelVisual3D>
    </Viewport3D>
</GroupBox>
 

Конструктор инициализации

 public View3D()
    {

        InitializeComponent();
        PositionCamera();

        for (int i = 0; i < LPDTest.points.Count; i  )
        {
            AddCubeToMesh(pointCloudMesh, points[i], 0.02);
        }
    }
 

XAML для части включения

 <Grid x:Name="My3dview">

</Grid>
 

Вот часть генерирующего кода (с пустыми данными)

 var rand = new Random();
while (true)
{
    My3dview.Children.Clear();
    points.Clear();
    for (int i = 0; i < 100; i  )
    {
        points.Add(new Point3D(rand.NextDouble(), rand.NextDouble(), rand.NextDouble()));
    }
    My3dview.Children.Add(new View3D());
}