Почему два преобразования не работают с объектом Helix Toolkit

Я собрал некоторый код, в котором я манипулирую двумя объектами Helix Toolkit Torus с помощью элементов управления ползунком WPF. Этот код является прототипом для другой более сложной задачи.

Когда я применяю только преобразование перемещения или вращения, объекты Тора перемещаются или вращаются. Однако, когда я применил оба, работает только последний из перечисленных в оде (Вращение в этом примере). Если я закомментирую Rotation Transform, переводы будут работать.

Я попробовал подход TransformGroup, тогда ничего не работает, кроме объектов зависимостей, определенных в наборе инструментов Helix (например, TorusDiameter).

Как видно из кода, одновременно должно работать только одно преобразование, так как за раз можно перемещать один ползунок.

У меня есть два вопроса:

(1) Почему несколько приложений (строк) Transform не работают? Как я могу заставить его работать?

(2) Я поместил окно сообщения в обработчик событий MyTorusChanged. Кажется, он стреляет только в начале. Я новичок в WPF. Как работает Transform без обработчика событий?

Модель просмотра:

// April 08, 2019, Dr. I. Konuk

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;

namespace AnimatedTorusDemo
{
    class MyTorusViewModel : ModelVisual3D
    {
        // origin of torus object
        public Point3D TorusOrigin { get; set; }
        public double translationX { get; set; }
        public double translationY { get; set; }
        public double translationZ { get; set; }

        // translation transformation parameter properties

        public static readonly DependencyProperty TranslationXProperty =
                    DependencyProperty.Register("TranslationX", typeof(double), typeof(MyTorusViewModel),
                            new UIPropertyMetadata(0.0));
        public static readonly DependencyProperty TranslationYProperty =
                    DependencyProperty.Register("TranslationY", typeof(double), typeof(MyTorusViewModel),
                            new UIPropertyMetadata(0.0));
        public static readonly DependencyProperty TranslationZProperty =
                    DependencyProperty.Register("TranslationZ", typeof(double), typeof(MyTorusViewModel),
                            new UIPropertyMetadata(0.0));

        public double TranslationX
        {
            get { return (double)GetValue(TranslationXProperty); }
            set { SetValue(TranslationXProperty, value); translationX = value; }
        }
        public double TranslationY
        {
            get { return (double)GetValue(TranslationYProperty); }
            set { SetValue(TranslationYProperty, value); translationY = value; }
        }
        public double TranslationZ
        {
            get { return (double)GetValue(TranslationZProperty); }
            set { SetValue(TranslationZProperty, value); translationZ = value; }
        }

        // MyTorusProperty
        // notice the callback function definition via Metadata

        public static DependencyProperty MyTorusProperty =
                    DependencyProperty.Register("MyTorus", typeof(TorusVisual3D), typeof(MyTorusViewModel),
                    new UIPropertyMetadata(null, MyTorusChanged));
        public TorusVisual3D MyTorus
        {
            get => (TorusVisual3D)GetValue(MyTorusProperty);
            set { SetValue(MyTorusProperty, value); }
        }

        // rotation angle property
        // Gets or sets the rotation angle (angle of torus).

        public static readonly DependencyProperty RotationAngleProperty =
                    DependencyProperty.Register("RotationAngle", typeof(double), typeof(MyTorusViewModel),
                            new UIPropertyMetadata(45.0));
        public double RotationAngle
        {
            get { return (double)GetValue(RotationAngleProperty); }
            set { SetValue(RotationAngleProperty, value); }
        }

        public static AxisAngleRotation3D axisAngleRotation = new AxisAngleRotation3D(new Vector3D(1, 0, 0), 0);

        public static readonly DependencyProperty TorusAxisAngleProperty =
                    DependencyProperty.Register("TorusAxisAngle", typeof(AxisAngleRotation3D), typeof(MyTorusViewModel),
                    new UIPropertyMetadata(axisAngleRotation));
        public AxisAngleRotation3D TorusAxisAngle
        {
            get { return (AxisAngleRotation3D)GetValue(TorusAxisAngleProperty);  }
            set { SetValue(TorusAxisAngleProperty, value);  }
        }

        // callback function used updating visuals (torus obejects)
        protected static void MyTorusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ((MyTorusViewModel)obj).UpdateVisuals();
        }

        // member function to update visuals
        private void UpdateVisuals()
        {
            MessageBox.Show("UpdateVisuals");

            Children.Clear();

            // each instance must have a torus object assigned for each MyTorusViewModel before

            if (MyTorus == null) return;

            MyTorus.ThetaDiv = 36;
            MyTorus.PhiDiv = 24;

            // translation

            var torusTranslate = new TranslateTransform3D(new Vector3D(translationX, translationY, translationZ));

            MyTorus.Transform = torusTranslate;

            // rotation

            var torusRotate = new RotateTransform3D();
            TorusAxisAngle = new AxisAngleRotation3D();

            TorusAxisAngle.Axis= new Vector3D(0, 1, 0);
            TorusAxisAngle.Angle = RotationAngle;

            torusRotate.Rotation = TorusAxisAngle;

            MyTorus.Transform = torusRotate;


            // transform group

            var torusTransformGroup = new Transform3DGroup();
            torusTransformGroup.Children.Add(torusTranslate);

            // MyTorus.Transform = torusTransformGroup;

            Children.Add(MyTorus);
        }

    }
}

xaml:

<Window x:Class="AnimatedTorusDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:h="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
        xmlns:local="clr-namespace:AnimatedTorusDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="750" Width="800">

    <Grid ShowGridLines="True">

        <Grid.RowDefinitions>
            <RowDefinition Height="4*" />
            <RowDefinition Height="0.75*" />
            <RowDefinition Height="0.75*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>

        <h:HelixViewport3D x:Name="TorusView" ZoomExtentsWhenLoaded="True"  ShowCoordinateSystem="True" PanGesture="LeftClick" >
            <h:DefaultLights/>
        </h:HelixViewport3D>

        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Bottom" Opacity="0.9" >

            <Slider x:Name="translationX1Slider" Value="0" Minimum="-5.0" Maximum="5.0" Width="150" Margin="10"
                    />
            <Slider x:Name="translationY1Slider" Value="0" Minimum="-5.0" Maximum="5.0" Width="150" Margin="10"
                    />
            <Slider x:Name="translationZ1Slider" Value="1" Minimum="-3" Maximum="3" Width="150" Margin="10"
                    />
            <Slider x:Name="rotation1Slider" Value="45" Minimum="0" Maximum="360" Width="150" Margin="10"
                    />

        </StackPanel>

        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Bottom" Opacity="0.9" >

            <Slider x:Name="translationX2Slider" Value="0" Minimum="-5.0" Maximum="5.0" Width="150" Margin="10"
                    />
            <Slider x:Name="translationY2Slider" Value="0" Minimum="-5" Maximum="5" Width="150" Margin="10"
                    />
            <Slider x:Name="translationZ2Slider" Value="-1" Minimum="-3" Maximum="3" Width="150" Margin="10"
                    />
            <Slider x:Name="rotation2Slider" Value="45" Minimum="0" Maximum="360" Width="150" Margin="10"
                    />

        </StackPanel>

        <StackPanel  Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Bottom" Opacity="0.9" >

            <Slider x:Name="torusDiameterSlider" Minimum="1" Maximum="10" Width="150" Margin="10"
                    />
            <Slider x:Name="tubeDiameterSlider" Value="1" Minimum="0.5" Maximum="5" Width="150" Margin="10"
                    />
            <Slider x:Name="thetaDivSlider" Value="36" Minimum="3" Maximum="256" Width="150" Margin="10"
                    />
            <Slider x:Name="phiDivSlider" Value="24" Minimum="3" Maximum="256" Width="150" Margin="10"/>

        </StackPanel>

    </Grid>
</Window>

главное окно:

// April 08, 2019, Dr. I. Konuk

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Diagnostics;
using System.Windows.Media.Media3D;

using HelixToolkit.Wpf;

namespace AnimatedTorusDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly ModelVisual3D model;
        private MyTorusViewModel visual1;
        private MyTorusViewModel visual2;
        public MainWindow()
        {
            InitializeComponent();

            model = new ModelVisual3D();

            visual1 = new MyTorusViewModel();

            var mytorus1 = new TorusVisual3D();
            mytorus1.Fill = Brushes.Red;
            visual1.MyTorus = mytorus1;

            visual2 = new MyTorusViewModel();

            var mytorus2 = new TorusVisual3D();
            mytorus2.Fill = Brushes.Blue;
            visual2.MyTorus = mytorus2;
            mytorus2.TorusDiameter = 5;

            // bindings with sliders in code

            // Helix Toolkit dependencyproperties:

            // a) Geometry:
            BindingOperations.SetBinding(mytorus1, HelixToolkit.Wpf.TorusVisual3D.TorusDiameterProperty, 
                                        new Binding("Value") { Source = torusDiameterSlider });
            BindingOperations.SetBinding(mytorus1, HelixToolkit.Wpf.TorusVisual3D.TubeDiameterProperty, 
                                        new Binding("Value") { Source = tubeDiameterSlider });

            // b) Translation:
            // Dependency properties defined in MyTorusView Model
            BindingOperations.SetBinding(mytorus1.Transform, TranslateTransform3D.OffsetXProperty, 
                                        new Binding("Value") { Source = translationX1Slider });
            BindingOperations.SetBinding(mytorus1.Transform, TranslateTransform3D.OffsetYProperty, 
                                        new Binding("Value") { Source = translationY1Slider });
            BindingOperations.SetBinding(mytorus1.Transform, TranslateTransform3D.OffsetZProperty,
                            new Binding("Value") { Source = translationZ1Slider });

            BindingOperations.SetBinding(mytorus2.Transform, TranslateTransform3D.OffsetXProperty,
                                        new Binding("Value") { Source = translationX2Slider });
            BindingOperations.SetBinding(mytorus2.Transform, TranslateTransform3D.OffsetYProperty,
                            new Binding("Value") { Source = translationY2Slider });
            BindingOperations.SetBinding(mytorus2.Transform, TranslateTransform3D.OffsetZProperty,
                new Binding("Value") { Source = translationZ2Slider });

            // b) Rotation
            BindingOperations.SetBinding(visual1.TorusAxisAngle, AxisAngleRotation3D.AngleProperty,
                             new Binding("Value") { Source = rotation1Slider });
            BindingOperations.SetBinding(visual2.TorusAxisAngle, AxisAngleRotation3D.AngleProperty,
                             new Binding("Value") { Source = rotation2Slider });


            model.Children.Add(visual1);
            model.Children.Add(visual2);
            TorusView.Children.Add(model);

            this.DataContext = this;
        }
    }
}

person I. Konuk    schedule 09.04.2019    source источник
comment
Здесь указано social.msdn.microsoft.com/Forums/vstudio/en-US/ похоже, что мы не можем использовать динамическую привязку ресурсов к 3D-элементам, созданным в коде. Может ли это быть причиной?   -  person I. Konuk    schedule 10.04.2019


Ответы (1)


Когда вы применяете поворот и устанавливаете его для свойства .transform, он заменяет предыдущее преобразование, которое включало перевод, новым, которое включает только поворот. Вместо этого вам нужно получить последнюю матрицу преобразования (включая перевод) и применить к ней вращение, а затем сбросить свойство .transform. Мой ответ на этот вопрос отвечает на первую часть вашего вопроса: Как повернуть 3D-модель с помощью HelixToolkit? Пожалуйста, задайте свой другой вопрос как отдельный вопрос (один вопрос на вопрос). Перейдите по этой ссылке по причинам: https://meta.stackexchange.com/questions/222735/can-i-ask-only-one-question-per-post

person Hassnain Ali    schedule 10.04.2019
comment
Хаснаин, СПАСИБО. Я бы никогда не догадался об этом! - person I. Konuk; 11.04.2019