Я собрал некоторый код, в котором я манипулирую двумя объектами 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;
}
}
}
Когда вы применяете поворот и устанавливаете его для свойства .transform, он заменяет предыдущее преобразование, которое включало перевод, новым, которое включает только поворот. Вместо этого вам нужно получить последнюю матрицу преобразования (включая перевод) и применить к ней вращение, а затем сбросить свойство .transform. Мой ответ на этот вопрос отвечает на первую часть вашего вопроса: Как повернуть 3D-модель с помощью HelixToolkit? Пожалуйста, задайте свой другой вопрос как отдельный вопрос (один вопрос на вопрос). Перейдите по этой ссылке по причинам: https://meta.stackexchange.com/questions/222735/can-i-ask-only-one-question-per-post
Хаснаин, СПАСИБО. Я бы никогда не догадался об этом!
Здесь social.msdn.microsoft.com/Forums/vstudio/en-US/… указано, что «похоже, мы не можем использовать динамическую привязку ресурсов к 3D-элементам, созданным в коде». Может ли это быть причиной?