目录
2. NuGet安装依赖包HelixToolkit.Wpf、WiimoteLib
1. 创建WPF项目WindDemo
2. NuGet安装依赖包HelixToolkit.Wpf、WiimoteLib
3. 编辑ModelView.xmal代码
<Window x:Class="WindDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:h="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf" Title="WindDemo" Height="480" Width="640">
<Grid>
<h:HelixViewport3D x:Name="view1" ZoomExtentsWhenLoaded="True">
<h:DefaultLights/>
</h:HelixViewport3D>
</Grid>
</Window>
4. 编辑ModelView.xmal.cs代码
namespace WindDemo
{
using System;
using System.Diagnostics;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
using WiimoteLib;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
private readonly ModelVisual3D model;
public MainWindow()
{
InitializeComponent();
model = new ModelVisual3D();
const int rows = 5;
const int columns = 4;
const double distance = 120;
var turbine = new WindTurbine();
var r = new Random();
for (int i = 0; i < rows; i++)
{
double y = i * distance;
for (int j = 0; j + (i % 2) * 0.5 <= columns - 1; j++)
{
double x = (j + (i % 2) * 0.5) * distance;
var visual = new WindTurbineVisual3D
{
RotationAngle = r.Next(360),
RotationSpeed = 20,
WindTurbine = turbine,
Transform = new TranslateTransform3D(x, y, 0)
};
model.Children.Add(visual);
}
}
var seasurface = new RectangleVisual3D
{
DivWidth = 100,
DivLength = 100,
Origin = new Point3D((rows - 2) * distance * 0.5, (columns) * distance * 0.5, 0),
Width = rows * distance * 2,
Length = columns * distance * 2
};
seasurface.Material = seasurface.BackMaterial = MaterialHelper.CreateMaterial(Colors.SeaGreen, 0.8);
model.Children.Add(new GridLinesVisual3D() { Center = seasurface.Origin, Fill = Brushes.Gray, Width = seasurface.Width, Length = seasurface.Length });
model.Children.Add(seasurface);
view1.Children.Add(model);
Loaded += MainWindowLoaded;
Closed += MainWindowClosed;
}
private Wiimote wm;
void MainWindowClosed(object sender, EventArgs e)
{
try
{
if (wm != null)
wm.Disconnect();
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
}
void MainWindowLoaded(object sender, System.Windows.RoutedEventArgs e)
{
try
{
wm = new Wiimote();
wm.WiimoteChanged += OnWiimoteChanged;
wm.Connect();
wm.SetReportType(InputReport.IRAccel, true);
wm.SetLEDs(false, false, false, false);
}
catch
{
wm = null;
}
}
private void OnWiimoteChanged(object sender, WiimoteChangedEventArgs e)
{
HeadTracking(e.WiimoteState.IRState);
}
// headtracking 'light'
// computes the angles to the observer around the camera up and right directions
// also calculates the distance between two IR points - used for scaling
// todo: take screen distance into account
private void HeadTracking(IRState irState)
{
if (irState.IRSensors[0].Found)
{
var p0 = irState.IRSensors[0].RawPosition;
double mx = p0.X;
double my = p0.Y;
double scale = 1;
if (irState.IRSensors[1].Found)
{
var p1 = irState.IRSensors[1].RawPosition;
double dx = p0.X - p1.X;
double dy = p0.Y - p1.Y;
double d = Math.Sqrt(dx * dx + dy * dy);
mx = (p0.X + p1.X) * 0.5;
my = (p0.Y + p1.Y) * 0.5;
scale = d / 200.0;
}
double theta = 20.0 * (mx - 512) / 512;
double phi = 20.0 * (my - 384) / 384;
Dispatcher.BeginInvoke(new Action(() => SetTransform(scale, theta, phi)));
}
//else
// Dispatcher.BeginInvoke(new Action(() => SetTransform(1, 0, 0)));
}
private void SetTransform(double scale, double theta, double phi)
{
var tg = new Transform3DGroup();
tg.Children.Add(new ScaleTransform3D(scale, scale, scale));
var center = view1.Camera.Position + view1.Camera.LookDirection;
var up = view1.Camera.UpDirection;
var right = Vector3D.CrossProduct(view1.Camera.LookDirection, up);
tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(up, theta), center));
tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(right, phi), center));
model.Transform = tg;
}
}
}
5. 创建WindTurbine.cs
namespace WindDemo
{
public class WindTurbine
{
// Rotor = Blades + Hub
public double BladeLength { get; set; }
public double BladeRootChord { get; set; }
public double BladeTipChord { get; set; }
public double HubDiameter { get; set; }
public int Blades { get; set; }
// Nacelle
public double NacelleDiameter { get; set; }
public double NacelleLength { get; set; }
public double Yaw { get; set; }
// Tower
public double BaseHeight { get; set; }
public double Height { get; set; }
public double Diameter { get; set; }
public double ShaftAngle { get; set; }
public double Overhang { get; set; }
public double Pre { get; set; }
public WindTurbine()
{
BladeLength = 40;
Height = 70;
BaseHeight = 20;
HubDiameter = 3;
Diameter = 4;
Overhang = 5;
NacelleLength = 8;
NacelleDiameter = 2;
BladeRootChord = 1;
BladeTipChord = 0.2;
Blades = 3;
ShaftAngle = 0.5;
Pre = -2.5;
}
}
}
6. 创建WindTurbineVisual3D.cs
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
namespace WindDemo
{
public class WindTurbineVisual3D : ModelVisual3D
{
public static readonly DependencyProperty WindTurbineProperty =
DependencyProperty.Register("WindTurbine", typeof(WindTurbine), typeof(WindTurbineVisual3D),
new UIPropertyMetadata(null, TurbineChanged));
public static readonly DependencyProperty RotationAngleProperty =
DependencyProperty.Register("RotationAngle", typeof(double), typeof(WindTurbineVisual3D),
new UIPropertyMetadata(0.0));
public int Blades
{
get { return WindTurbine.Blades; }
set
{
WindTurbine.Blades = value;
UpdateVisuals();
}
}
public double Height
{
get { return WindTurbine.Height; }
set
{
WindTurbine.Height = value;
UpdateVisuals();
}
}
public WindTurbine WindTurbine
{
get { return (WindTurbine)GetValue(WindTurbineProperty); }
set { SetValue(WindTurbineProperty, value); }
}
/// <summary>
/// Gets or sets the rotation angle (angle of the rotor).
/// </summary>
/// <value>The rotation angle.</value>
public double RotationAngle
{
get { return (double)GetValue(RotationAngleProperty); }
set { SetValue(RotationAngleProperty, value); }
}
/// <summary>
/// Gets or sets the rotation speed (rpm).
/// </summary>
/// <value>The rotation speed.</value>
public double RotationSpeed
{
get { return (double)GetValue(RotationSpeedProperty); }
set { SetValue(RotationSpeedProperty, value); }
}
public static readonly DependencyProperty RotationSpeedProperty =
DependencyProperty.Register("RotationSpeed", typeof(double), typeof(WindTurbineVisual3D), new UIPropertyMetadata(10.0));
protected static void TurbineChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
((WindTurbineVisual3D)obj).UpdateVisuals();
}
private readonly Stopwatch watch = new Stopwatch();
private readonly RenderingEventListener renderingEventListener;
public WindTurbineVisual3D()
{
renderingEventListener = new RenderingEventListener(this.OnCompositionTargetRendering);
RenderingEventManager.AddListener(renderingEventListener);
}
private void OnCompositionTargetRendering(object sender, RenderingEventArgs e)
{
double delta = watch.ElapsedMilliseconds * 0.001;
RotationAngle += 360 * RotationSpeed / 60 * delta;
watch.Restart();
}
private void UpdateVisuals()
{
Children.Clear();
if (WindTurbine == null) return;
var baseTower = new TruncatedConeVisual3D
{
Fill = Brushes.Yellow,
Origin = new Point3D(0, 0, -WindTurbine.BaseHeight)
};
baseTower.Height = -baseTower.Origin.Z + 2;
baseTower.BaseRadius = baseTower.TopRadius = WindTurbine.Diameter;
var tower = new TruncatedConeVisual3D
{
Fill = Brushes.White,
Origin = new Point3D(0, 0, 2),
Height = WindTurbine.Height,
BaseRadius = WindTurbine.Diameter
};
tower.TopRadius = tower.BaseRadius * (1 - WindTurbine.Height * Math.Sin(WindTurbine.ShaftAngle / 180.0 * Math.PI));
var nacelle = new TruncatedConeVisual3D
{
Fill = Brushes.White,
Origin = new Point3D(WindTurbine.Overhang, 0, tower.Origin.Z + tower.Height),
Normal = new Vector3D(-1, 0, 0),
TopRadius = WindTurbine.NacelleDiameter
};
nacelle.BaseRadius = nacelle.TopRadius * 0.7;
nacelle.Height = WindTurbine.NacelleLength;
Children.Add(baseTower);
Children.Add(tower);
Children.Add(nacelle);
var endcap = new SphereVisual3D
{
Center = new Point3D(WindTurbine.Overhang - WindTurbine.NacelleLength, 0,
tower.Origin.Z + tower.Height),
Radius = nacelle.TopRadius,
Fill = Brushes.White
};
Children.Add(endcap);
var rotor = new ModelVisual3D();
for (int i = 0; i < WindTurbine.Blades; i++)
{
double angle = (double)i / WindTurbine.Blades * Math.PI * 2;
// todo: the blade is simplified to a cone... it should be a real profile...
var blade = new TruncatedConeVisual3D
{
Origin = nacelle.Origin,
Normal = new Vector3D(0, Math.Cos(angle), Math.Sin(angle)),
Height = WindTurbine.BladeLength,
BaseRadius = WindTurbine.BladeRootChord,
TopRadius = WindTurbine.BladeTipChord,
Fill = Brushes.White
};
rotor.Children.Add(blade);
}
var hub = new SphereVisual3D
{
Fill = Brushes.White,
Center = nacelle.Origin,
Radius = WindTurbine.HubDiameter / 2
};
rotor.Children.Add(hub);
Children.Add(rotor);
var rotation = new AxisAngleRotation3D(new Vector3D(1, 0, 0), 0);
var rotorTransform = new RotateTransform3D(null, hub.Center) { Rotation = rotation };
rotor.Transform = rotorTransform;
var b = new Binding("RotationAngle") { Source = this };
BindingOperations.SetBinding(rotation, AxisAngleRotation3D.AngleProperty, b);
}
}
}
7. 启动调试