Почему два преобразования не работают с объектом 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;
        }
    }
}

Здесь social.msdn.microsoft.com/Forums/vstudio/en-US/… указано, что «похоже, мы не можем использовать динамическую привязку ресурсов к 3D-элементам, созданным в коде». Может ли это быть причиной?

I. Konuk 10.04.2019 16:01
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
1 013
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Хаснаин, СПАСИБО. Я бы никогда не догадался об этом!

I. Konuk 11.04.2019 01:44

Другие вопросы по теме