1.AnaglyphDemo 浮雕效果
<!--########### AnaglyphDemo 一个浮雕/立体影片查看器控件。###########-->
<Window x:Class="AnaglyphDemo.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"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:AnaglyphDemo"
Title="AnaglyphDemo" Height="480" Width="640">
<Window.Resources> <!-- 窗体资源 -->
<!--静态资源 对象数据提供器:关键词 methods 方法名:GetValues 对象类型:Enum-->
<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type System:Enum}" x:Key="methods">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="h:AnaglyphMethod" /> <!-- 方法参数 类型:浮雕方法 AnaglyphMethod -->
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<!-- 画刷brush1 -->
<DrawingBrush x:Key="brush1" ViewportUnits="Absolute" Viewport="0,0,0.1,0.1" Viewbox="0,0,1,1" ViewboxUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White"> <!-- 白色 -->
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1"/> <!-- -->
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black"> <!-- 黑色 -->
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0.25,0.25,0.5,0.5"/><!-- -->
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
<!-- 半潜式 -->
<ModelVisual3D x:Key="SemiSubmersible">
<!--桌面及其上面 共计4个box -->
<h:BoxVisual3D Width="50" Height="4" Length="50" Center="0,0,12" Fill="White"/>
<h:BoxVisual3D Width="6" Height="20" Length="6" Center="0,0,24" Fill="Yellow"/>
<h:BoxVisual3D Width="10" Height="4" Length="30" Center="0,10,16" Fill="White"/>
<h:BoxVisual3D Width="10" Height="4" Length="10" Center="10,10,20" Fill="White"/>
<!-- 四个柱子 -->
<h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="-20,-20,-10" Height="20" Fill="Green"/>
<h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="-20,20,-10" Height="20" Fill="Green"/>
<h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="20,-20,-10" Height="20" Fill="Green"/>
<h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="20,20,-10" Height="20" Fill="Green"/>
<!-- 柱子之间4个连接杆 -->
<h:BoxVisual3D Center="-20,0,-6" Height="4" Width="40" Length="4" Fill="Gray"/>
<h:BoxVisual3D Center="20,0,-6" Height="4" Width="40" Length="4" Fill="Gray"/>
<h:BoxVisual3D Center="0,-20,-6" Height="4" Width="4" Length="40" Fill="Gray"/>
<h:BoxVisual3D Center="0,20,-6" Height="4" Width="4" Length="40" Fill="Gray"/>
<h:GridLinesVisual3D Fill="White"/>
</ModelVisual3D>
</Window.Resources>
<!-- 停靠面板:停靠顶部, 菜单项File -->
<DockPanel >
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
</MenuItem>
</Menu>
<!-- 状态栏 -->
<StatusBar DockPanel.Dock="Bottom">
<Label Content="Model:" Margin="0 0 4 0"/>
<!-- 下拉列表框对象:ModelItem 显示title -->
<ComboBox Name="model" DisplayMemberPath="Title" Width="100" SelectionChanged="model_SelectionChanged">
<local:ModelItem Title="Cubes"/>
<local:ModelItem Title="Semi-submersible" Model="{StaticResource SemiSubmersible}"/> <!-- 通过xaml设置下拉列表框第二项 -->
</ComboBox>
<Label Content="Method:" Margin="10 0 4 0"/>
<!-- 下拉列表框 项源绑定:静态资源 methods-->
<ComboBox Name="method" ItemsSource="{Binding Source={StaticResource methods}}" SelectedIndex="5" Width="100"/>
<Label Content="Stereobase:" Margin="10 0 4 0"/>
<!-- 滑动条 -->
<Slider Name="stereoBase" Minimum="0" Maximum="5" Value="0.15" Width="100"/>
<!-- 文本框绑定滑动条的值 -->
<TextBlock Text="{Binding Value, ElementName=stereoBase, StringFormat={}{0:0.000}}" Padding="4 0 4 0"/>
</StatusBar>
<!-- 浮雕3D视图 StereoBase属性绑定 滑动条的值 -->
<h:AnaglyphView3D x:Name="anaglyphView1" Background="Black" StereoBase="{Binding Value, ElementName=stereoBase}" Method="{Binding SelectedValue, ElementName=method}"
CopyDirectionVector="False"/>
</DockPanel>
</Window>
<!-- 主窗体交互逻辑 MainWindow : Window
namespace AnaglyphDemo
{
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using ExampleBrowser;
using HelixToolkit.Wpf;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
[Example(null, "Showing a stereo view using the AnaglyphView3D control.")]
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//通过代码设置 下拉列表框 数据项第一项
var cubes = new ModelVisual3D();
AddCubes(cubes.Children, Brushes.White, 0);
AddCubes(cubes.Children, Brushes.Green, -4);
AddCubes(cubes.Children, Brushes.Green, 4);
((ModelItem)model.Items[0]).Model = cubes;
Loaded += OnLoaded;
}
//添加立方体
private void AddCubes(Visual3DCollection c, Brush brush, double x)
{
for (double y = -5; y <= 5; y += 10)
c.Add(new CubeVisual3D { Fill = brush, Center = new Point3D(x, y, 0) });
}
//默认选择第一项model
void OnLoaded(object sender, RoutedEventArgs e)
{
model.SelectedIndex = 0;
}
//下拉列表框改变
private void model_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (anaglyphView1 == null || anaglyphView1.Children.Count == 0)//浮雕3D视图为null或者没有子对象
return;
var lights = anaglyphView1.Children[0];//获取光源
anaglyphView1.Clear();//清空视图
var m = model.SelectedItem as ModelItem;//获取模型数据项
anaglyphView1.Children.Add(lights);//添加光源
anaglyphView1.Children.Add(m.Model);//添加模型
anaglyphView1.SynchronizeStereoModel();//同步立体模型
// anaglyphView1.ZoomExtents();
}
}
//获取 可见模型对象
public class ModelItem
{
public string Title { get; set; } //标题
public ModelVisual3D Model { get; set; }//3D可见模型
}
}
-->
2. BackgroundUpdate 后台线程
<!--########### BackgroundUpdate 后台线程更新几何体 ###########-->
<Window x:Class="BackgroundUpdateDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:h="http://helix-toolkit.org/wpf"
Title="Background worker update" Height="480" Width="640">
<Grid>
<!-- 3D场景:4个模型 -->
<h:HelixViewport3D>
<h:SunLight/>
<ModelVisual3D x:Name="model1"/>
<ModelVisual3D x:Name="model2"/>
<ModelVisual3D x:Name="model3"/>
<ModelVisual3D x:Name="model4"/>
</h:HelixViewport3D>
<!--堆栈面板: 4个复选框-4个布尔变量 添加点、添加冻结的几何体、添加冻结模型、添加到模型组 -->
<StackPanel Margin="8">
<CheckBox IsChecked="{Binding AddPoints}" Content="Points (blue)"/>
<CheckBox IsChecked="{Binding AddFrozenGeometry}" Content="Geometry (red)"/>
<CheckBox IsChecked="{Binding AddFrozenModel}" Content="Model (green)"/>
<CheckBox IsChecked="{Binding AddToModelGroup}" Content="ModelGroup (gold)"/>
</StackPanel>
<!-- 堆栈面板:4个文本框,绑定 4个后台线程执行次数-->
<StackPanel Margin="8" VerticalAlignment="Bottom">
<TextBlock Text="{Binding Count1, StringFormat='Points: {0}'}"/>
<TextBlock Text="{Binding Count2, StringFormat='Geometry: {0}'}"/>
<TextBlock Text="{Binding Count3, StringFormat='Model: {0}'}"/>
<TextBlock Text="{Binding Count4, StringFormat='ModelGroup: {0}'}"/>
</StackPanel>
</Grid>
</Window>
<!-- 主界面交互逻辑 MainWindow : Window, INotifyPropertyChanged
namespace BackgroundUpdateDemo
{
using System;
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Windows.Threading;
using ExampleBrowser;
using HelixToolkit.Wpf;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
[Example(null, "Updates the visual model in a background thread.")]
public partial class MainWindow : Window, INotifyPropertyChanged
{ //https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.cancellationtokensource?view=net-6.0
//向System.Threading.CancellationToken发出应该取消的信号。
private CancellationTokenSource source;//对象通过其 Token 属性提供取消令牌,并通过调用取消 Cancel 消息或 CancelAfter 方法发送取消消息。
Material mat2 = MaterialHelper.CreateMaterial(Colors.Red);//红色材质
int n = 10;//如果太卡,减小n
//四种颜色,四个计数
private int count1;
private int count2;
private int count3;
private int count4;
private int runningWorkers;//运行的后台数
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public MainWindow()
{
this.InitializeComponent();
this.DataContext = this;//更新数据绑定上下文
this.Loaded += this.MainWindowLoaded;
this.Closing += this.MainWindowClosing;
this.AddPoints = true;//添加点
this.AddFrozenGeometry = true;//添加冻结的几何体
this.AddFrozenModel = true;
this.AddToModelGroup = true;
}
public bool AddPoints { get; set; }//是否添加点
public bool AddFrozenGeometry { get; set; }//是否添加冻结几何体
public bool AddFrozenModel { get; set; }//添加冻结模型
public bool AddToModelGroup { get; set; }//添加到模型组
public int Count1
{
get
{
return this.count1;
}
set
{
this.count1 = value;
this.OnPropertyChanged("Count1");
}
}
public int Count2
{
get
{
return this.count2;
}
set
{
this.count2 = value;
this.OnPropertyChanged("Count2");
}
}
public int Count3
{
get
{
return this.count3;
}
set
{
this.count3 = value;
this.OnPropertyChanged("Count3");
}
}
public int Count4
{
get
{
return this.count4;
}
set
{
this.count4 = value;
this.OnPropertyChanged("Count4");
}
}
//关闭窗口时,取消令牌。
void MainWindowClosing(object sender, CancelEventArgs e)
{
this.source.Cancel();//沟通取消请求。
}
void MainWindowLoaded(object sender, RoutedEventArgs e)
{
Materials.Gold.Freeze();//使当前对象不可修改,并将其System.Windows.Freezable.IsFrozen属性设置为true
this.source = new CancellationTokenSource();//初始化System.Threading.CancellationTokenSource。
// this.Dispatcher获取System.Windows.Threading.DispatcherObjec所关联的System.Windows.Threading.Dispatcher。
Task.Factory.StartNew(this.Worker1, this.Dispatcher, source.Token);
Task.Factory.StartNew(this.Worker2, this.Dispatcher, source.Token);
Task.Factory.StartNew(this.Worker3, this.Dispatcher, source.Token);
Task.Factory.StartNew(this.Worker4, this.Dispatcher, source.Token);
}
//后台线程1:第2象限 蓝色 添加盒子到model1 nxnxn个
private void Worker1(object d)
{
var dispatcher = (Dispatcher)d;
Interlocked.Increment(ref this.runningWorkers);//为由多个线程共享的变量提供原子操作。增加指定变量并将结果作为原子操作存储。
while (!this.source.IsCancellationRequested)
{
if (!this.AddPoints || this.runningWorkers < 4)
{
Thread.Yield();//导致调用线程将执行让出给准备在当前处理器上运行的另一个线程。操作系统选择要屈从的线程。
continue; //4个后台线程都开始执行之后,才开始绘制
}
for (int i = 1; i <= n && this.AddPoints; i++)
{
for (int j = 1; j <= n && this.AddPoints; j++)
{
for (int k = 1; k <= n && this.AddPoints; k++)
{ //调用add方法 添加盒子到model1。 Point3D(-i, j, k)
dispatcher.Invoke(new Action<Point3D, ModelVisual3D>(this.Add), new Point3D(-i, j, k), this.model1);
}
}
}
dispatcher.Invoke((Action)(() => this.Count1++));//后台线程1执行次数++
dispatcher.Invoke(new Action<ModelVisual3D>(this.Clear), this.model1);//清空model1显示
}
}
//添加红色盒子到model2 第1象限
private void Worker2(object d)
{
var dispatcher = (Dispatcher)d;
Interlocked.Increment(ref this.runningWorkers);
while (!this.source.IsCancellationRequested)
{
if (!this.AddFrozenGeometry || this.runningWorkers < 4)
{
Thread.Yield();
continue;
}
for (int i = 1; i <= n; i++)
{
var b = new MeshBuilder();
for (int j = 1; j <= n; j++)
{
for (int k = 1; k <= n; k++)
{
b.AddBox(new Point3D(i, j, k), 0.8, 0.8, 0.8);//添加盒子 Point3D(i, j, k)
}
}
dispatcher.Invoke(new Action<MeshGeometry3D, Material, ModelVisual3D>(this.Add), b.ToMesh(true), mat2, model2);//添加所有新增盒子的网格面到model2
}
dispatcher.Invoke((Action)(() => this.Count2++));//线程2执行次数
dispatcher.Invoke(new Action<ModelVisual3D>(this.Clear), this.model2);//清空模型2显示
}
}
//后台线程3: 第5象限 绿色盒子
private void Worker3(object d)
{
var dispatcher = (Dispatcher)d;
var m = MaterialHelper.CreateMaterial(Colors.Green);//绿色
Interlocked.Increment(ref this.runningWorkers);//运行后台线程数
while (!this.source.IsCancellationRequested)
{
if (!this.AddFrozenModel || this.runningWorkers < 4)
{
Thread.Yield();
continue;
}
for (int i = 1; i <= n; i++)
{
var b = new MeshBuilder();
for (int j = 1; j <= n; j++)
{
for (int k = 1; k <= n; k++)
{
b.AddBox(new Point3D(i, j, -k), 0.8, 0.8, 0.8);//添加盒子Point3D(i, j, -k)
}
}
var box = new GeometryModel3D { Geometry = b.ToMesh(false), Material = m };
box.Freeze();//新建几何体,固定元素。。使当前对象不可修改,并将其System.Windows.Freezable.IsFrozen属性设置为true。
dispatcher.Invoke(new Action<Model3D, ModelVisual3D>(this.Add), box, this.model3);//添加box到model3并显示。
}
dispatcher.Invoke((Action)(() => this.Count3++));
dispatcher.Invoke(new Action<ModelVisual3D>(this.Clear), model3);//清空model3,box没有消失?
}
}
//后台线程4:第6象限
private void Worker4(object d)
{
var dispatcher = (Dispatcher)d;
var m = MaterialHelper.CreateMaterial(Colors.Gold);//金色材质固定
Model3DGroup mg = null;
dispatcher.Invoke(new Action(() => this.model4.Content = mg = new Model3DGroup()));//初始化model4模型组
Interlocked.Increment(ref this.runningWorkers);
while (!this.source.IsCancellationRequested)
{
if (!this.AddToModelGroup || this.runningWorkers < 4)
{
Thread.Yield();
continue;
}
for (int i = 1; i <= n; i++)
{
var b = new MeshBuilder();
for (int j = 1; j <= n; j++)
{
for (int k = 1; k <= n; k++)
{
b.AddBox(new Point3D(-i, j, -k), 0.8, 0.8, 0.8);//添加盒子Point3D(-i, j, -k)
}
}
var box = new GeometryModel3D { Geometry = b.ToMesh(false), Material = m };//金色材质
box.Freeze();//盒子固定
dispatcher.Invoke(new Action(() => mg.Children.Add(box)));//添加box到模型组model4
}
dispatcher.Invoke((Action)(() => this.Count4++));
dispatcher.Invoke((Action)(() => mg.Children.Clear()));//清空model4
}
}
//清空3D可见模型
private void Clear(ModelVisual3D visual)
{
visual.Children.Clear();
}
//后台线程1添加盒子到 model1
private void Add(Point3D p, ModelVisual3D visual)
{
// 在 UI 线程上创建可视化box
var box = new BoxVisual3D { Center = p, Width = 0.8, Height = 0.8, Length = 0.8 };
visual.Children.Add(box);
}
//后台线程2添加网格几何体到 model2
private void Add(MeshGeometry3D g, Material m, ModelVisual3D visual)
{
var box = new GeometryModel3D { Geometry = g, Material = m };
visual.Children.Add(new ModelVisual3D { Content = box });
}
//后台线程3添加model到model3
private void Add(Model3D model, ModelVisual3D visual)
{
visual.Children.Add(new ModelVisual3D { Content = model });
}
//属性值改变
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
-->
3. Billboard 广告牌
<!--########### Billboard 广告牌 ###########-->
<Window x:Class="BillboardDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ht="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
Title="BillboardDemo" Height="480" Width="640">
<Grid>
<ht:HelixViewport3D ZoomExtentsWhenLoaded="True">
<!-- 添加一些光源 -->
<ht:SunLight/>
<!-- 路 -->
<ht:RectangleVisual3D Width="10" Length="60" Origin="0,30,0" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material Gray}"/>
<!-- 黄色线条 -->
<ht:RectangleVisual3D Width="0.1" Length="60" Origin="0.1,30,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material Yellow}"/>
<ht:RectangleVisual3D Width="0.1" Length="60" Origin="-0.1,30,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material Yellow}"/>
<!--通过复制/粘贴的斑马线 10条,宽0.5,长5,方向Y,法线z。白色。 A Zebra crossing by copy/paste :) -->
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="4.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="3.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="2.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="1.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="0.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="-0.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="-1.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="-2.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="-3.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<ht:RectangleVisual3D Width="0.5" Length="5" Origin="-4.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
<!--标志,看起来不太好,但显示了广告牌的工作原理 IsEmissive自发光,图片材质 Signs, not looking good, but shows how the billboards work -->
<ht:BillboardVisual3D Position="6,10,3" Width="40" Height="40" Material="{ht:ImageMaterial 'pack://application:,,,/Examples/Billboard/roadsign.png', IsEmissive=true}"/>
<ht:BillboardVisual3D Position="6,30,3" Width="40" Height="40" Material="{ht:ImageMaterial 'pack://application:,,,/Examples/Billboard/roadsign.png', IsEmissive=true}"/>
<ht:BillboardVisual3D Position="6,50,3" Width="40" Height="40" Material="{ht:ImageMaterial 'pack://application:,,,/Examples/Billboard/roadsign.png', IsEmissive=true}"/>
<!-- 标志杆Sign poles -->
<ht:TruncatedConeVisual3D Origin="6,10,0" Height="3" BaseRadius="0.1" TopRadius="0.1" Material="{ht:Material LightGray}"/>
<ht:TruncatedConeVisual3D Origin="6,30,0" Height="3" BaseRadius="0.1" TopRadius="0.1" Material="{ht:Material LightGray}"/>
<ht:TruncatedConeVisual3D Origin="6,50,0" Height="3" BaseRadius="0.1" TopRadius="0.1" Material="{ht:Material LightGray}"/>
<!--文字广告牌,始终面向相机 Text billboards, always facing camera-->
<ht:BillboardTextVisual3D Position="5,0,0" Text="5,0,0" DepthOffset="0.01"/>
<ht:BillboardTextVisual3D Position="-5,0,0" Text="-5,0,0" DepthOffset="0.01"/>
<ht:BillboardTextVisual3D Position="5,60,0" Text="5,60,0" DepthOffset="0.01"/>
<ht:BillboardTextVisual3D Position="-5,60,0" Text="-5,60,0" DepthOffset="0.01"/>
</ht:HelixViewport3D>
</Grid>
</Window>
4. BuildingDemo 建筑物
<!--########### BuildingDemo 建筑物 模型绑定界面设置 ###########-->
<Window x:Class="BuildingDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:h="http://helix-toolkit.org/wpf"
xmlns:buildingDemo="clr-namespace:BuildingDemo"
xmlns:pt="http://propertytools.org/wpf"
xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=System"
Title="BuildingDemo" Height="720" Width="1080">
<Window.Resources> <!-- 窗体资源 -->
<ImageBrush x:Key="SmokeBrush" ImageSource="smoke.png"/> <!-- 图片刷子:烟雾图片 -->
<ImageBrush x:Key="FenceBrush" ImageSource="fence.png" ViewportUnits="Absolute" TileMode="Tile"/> <!-- 图片刷子: 围栏图片 -->
<DrawingBrush x:Key="KerbBrush" ViewportUnits="Absolute" TileMode="Tile"> <!-- 路边刷子 -->
<DrawingBrush.Drawing>
<DrawingGroup> <!-- 绘制组:黑色矩形,半个黄色矩形 -->
<GeometryDrawing Brush="Black"> <!-- 黑色,单位矩形 -->
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Yellow"> <!-- 黄色,半个矩形 -->
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,0.5,1" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
<buildingDemo:BoundsConverter x:Key="BoundsConverter"></buildingDemo:BoundsConverter> <!-- 边界转换器类对象:选定对象后显示边界框 -->
<Point3DCollection x:Key="FencePositions"> <!-- 围栏位置 3d点集 -->
<Point3D X="-22" Y="78" Z="0"/>
<Point3D X="72" Y="78" Z="0"/>
<Point3D X="72" Y="172" Z="0"/>
<Point3D X="-22" Y="172" Z="0"/>
<Point3D X="-22" Y="78" Z="0"/>
</Point3DCollection>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions> <!-- 两列 -->
<ColumnDefinition Width="1.61*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!-- 3D视图 -->
<h:HelixViewport3D ZoomExtentsWhenLoaded="True" MouseDown="UIElement_OnMouseDown" Background="LightGray" IsHeadLightEnabled="True" ZoomAroundMouseDownPoint="True" RotateAroundMouseDownPoint="True">
<ModelVisual3D>
<!-- 厂房3D -->
<buildingDemo:HouseVisual3D/>
<buildingDemo:HouseVisual3D Transform="{h:Translate 40,0,0}"/>
<buildingDemo:HouseVisual3D Transform="{h:Translate 0,20,0}"/>
<buildingDemo:HouseVisual3D Transform="{h:Translate 40,20,0}"/>
<!-- 默认筒仓3D -->
<buildingDemo:SiloVisual3D Transform="{h:Translate 0,100,0}"/>
<buildingDemo:SiloVisual3D Transform="{h:Translate 50,100,0}"/>
<buildingDemo:SiloVisual3D Transform="{h:Translate 0,150,0}"/>
<buildingDemo:SiloVisual3D Transform="{h:Translate 50,150,0}"/>
<!-- 自定义筒仓 -->
<buildingDemo:SiloVisual3D Transform="{h:Translate 100,125,0}" Height="40" Diameter="20" DomeDiameter="18" DomeHeight="3"/>
<buildingDemo:SiloVisual3D Transform="{h:Translate 130,125,0}" Height="40" Diameter="20" DomeDiameter="18" DomeHeight="3"/>
<buildingDemo:SiloVisual3D Transform="{h:Translate 160,125,0}" Height="40" Diameter="20" DomeDiameter="18" DomeHeight="3"/>
<!-- 路边模型 -->
<buildingDemo:KerbVisual3D Transform="{h:Translate 130,125,0}" Width="0.2" Height="0.1" Positions="{buildingDemo:Superellipse 45,15,40}" Texture="{StaticResource KerbBrush}"/>
<!-- 烟囱3D -->
<buildingDemo:ChimneyVisual3D x:Name="chimney1" Position="100,0,0"/>
<buildingDemo:ChimneyVisual3D Position="120,0,0"/>
<!-- 篱笆3D -->
<buildingDemo:FenceVisual3D Height="2" Transform="{h:Translate 110,0,0}" Positions="{buildingDemo:Superellipse 20,20,3}" PoleTexture="Silver" FenceTexture="{StaticResource FenceBrush}"/>
<buildingDemo:FenceVisual3D Height="2" Positions="{StaticResource FencePositions}" PoleTexture="Silver" FenceTexture="{StaticResource FenceBrush}"/>
</ModelVisual3D>
<ModelVisual3D><!-- 光照:散射光-白色 -->
<ModelVisual3D.Content>
<AmbientLight Color="White"/>
</ModelVisual3D.Content>
<!-- 粒子系统:纹理,位置绑定chimney1的TopPosition -->
<h:ParticleSystem Texture="{StaticResource SmokeBrush}" Position="{Binding TopPosition, ElementName=chimney1}"/>
</ModelVisual3D>
<!-- 边界框3D :绑定选择对象, 边界框转换器 -->
<h:BoundingBoxVisual3D BoundingBox="{Binding SelectedObject, Converter={StaticResource BoundsConverter}}" Diameter="0.2"/>
</h:HelixViewport3D>
<!-- 属性网格 1列,选择对象SelectedObject,可浏览属性BrowsableAttribute。 选择对象后右侧网格内显示属性。-->
<pt:PropertyGrid Grid.Column="1" SelectedObject="{Binding SelectedObject}" TabVisibility="Collapsed" Margin="4" RequiredAttribute="{x:Type componentModel:BrowsableAttribute}"/>
</Grid>
</Window>
<!-- 主窗体交互逻辑: 单击对象,选择对象。
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media.Media3D;
using ExampleBrowser;
using HelixToolkit.Wpf;
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
[Example("BuildingDemo", "Using MeshBuilder to create buildings.")]
public partial class Window1 : Window
{
private ViewModel viewModel;
public Window1()
{
this.InitializeComponent();
this.DataContext = this.viewModel = new ViewModel();//数据上下文: 视图模型
}
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var viewport = (HelixViewport3D)sender;//获取3D视口
var firstHit = viewport.Viewport.FindHits(e.GetPosition(viewport)).FirstOrDefault();//找到单击对象//
if (firstHit != null)
{
this.viewModel.Select(firstHit.Visual);//选择对象的可见模型//
}
else
{
this.viewModel.Select(null);//选择null
}
}
}
-->
<!-- 边界转换:为指定的可视对象查找边界框。
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
[ValueConversion(typeof(Visual3D), typeof(Rect3D))]
public class BoundsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var visual = value as Visual3D;
return visual != null ? visual.FindBounds(Transform3D.Identity) : Rect3D.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
-->
<!--烟囱3D: 注册依赖属性 烟囱3d继承自 UI元素3D,可点击。
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
using PropertyTools.DataAnnotations;//属性工具-数据注释
using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;//可浏览属性
public class ChimneyVisual3D : UIElement3D //烟囱3d继承自 UI元素3D,可点击。
{
public static readonly DependencyProperty PositionProperty = DependencyPropertyEx.Register<Point3D, ChimneyVisual3D>("Position", new Point3D(0, 0, 0), (s, e) => s.AppearanceChanged()); //注册烟囱位置依赖属性 默认【0,0,0】
public static readonly DependencyProperty TopPositionProperty = DependencyPropertyEx.Register<Point3D, ChimneyVisual3D>("TopPosition", new Point3D(0, 0, 0), (s, e) => s.AppearanceChanged());//注册顶部位置依赖属性 默认【0,0,0】
public static readonly DependencyProperty BaseDiameterProperty = DependencyPropertyEx.Register<double, ChimneyVisual3D>("BaseDiameter", 6, (s, e) => s.AppearanceChanged()); //注册基座直径依赖属性 默认6
public static readonly DependencyProperty TopDiameterProperty = DependencyPropertyEx.Register<double, ChimneyVisual3D>("TopDiameter", 3, (s, e) => s.AppearanceChanged()); //注册顶部直径依赖属性 默认3
public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, ChimneyVisual3D>("Height", 80, (s, e) => s.AppearanceChanged()); //注册 高度依赖属性,默认80
public static readonly DependencyProperty BandsProperty = DependencyPropertyEx.Register<int, ChimneyVisual3D>("Bands", 8, (s, e) => s.AppearanceChanged()); //注册 条纹带段数 依赖属性 默认8
private readonly GeometryModel3D redbands = new GeometryModel3D();// 红色带
private readonly GeometryModel3D whitebands = new GeometryModel3D();//白色条带
public ChimneyVisual3D()
{
this.redbands.Material = MaterialHelper.CreateMaterial(Brushes.Red, ambient: 10); //设置红色条带颜色材质--红色刷子
this.whitebands.Material = MaterialHelper.CreateMaterial(Brushes.White, ambient: 10);//设置白色条带材质-白色刷子
this.AppearanceChanged();//更新烟囱外观
var group = new Model3DGroup();//
group.Children.Add(this.redbands);//红色带
group.Children.Add(this.whitebands);//白色带
this.Visual3DModel = group;//设置UIElement3D烟囱的可见模型为红色带+白色带
}
//位置属性: get依赖属性的值强制转换为Point3D; 设置依赖属性的值。
public Point3D Position
{
get { return (Point3D)this.GetValue(PositionProperty); }
set { this.SetValue(PositionProperty, value); }
}
//烟囱顶部位置属性
public Point3D TopPosition
{
get { return (Point3D)this.GetValue(TopPositionProperty); }
set { this.SetValue(TopPositionProperty, value); }
}
//烟囱基座直径属性 注释Chimney properties;可滑动[0-20];字符串格式[0.00];可浏览属性;
[Category("Chimney properties")]
[Slidable(0, 20)]
[FormatString("0.00")]
[Browsable(true)]
public double BaseDiameter
{
get { return (double)this.GetValue(BaseDiameterProperty); }
set { this.SetValue(BaseDiameterProperty, value); }
}
//烟囱顶部直径属性:可滑动条设置0-20,可浏览属性。
[Slidable(0, 20)]
[FormatString("0.00")]
[Browsable(true)]
public double TopDiameter
{
get { return (double)this.GetValue(TopDiameterProperty); }
set { this.SetValue(TopDiameterProperty, value); }
}
//烟囱高度属性 ,可滑动0-100,可浏览。
[Slidable(0, 100)]
[FormatString("0.00")]
[Browsable(true)]
public double Height
{
get { return (double)this.GetValue(HeightProperty); }
set { this.SetValue(HeightProperty, value); }
}
//烟囱条带属性:可滑动1-20,可浏览。
[Slidable(1, 20)]
[FormatString("0.00")]
[Browsable(true)]
public int Bands
{
get { return (int)this.GetValue(BandsProperty); }
set { this.SetValue(BandsProperty, value); }
}
//外观改变函数
private void AppearanceChanged()
{
var redbuilder = new MeshBuilder(false, false); //红色条带网格构建器
var whitebuilder = new MeshBuilder(false, false);//白色条带网格构建器
for (int i = 0; i < this.Bands; i++)
{
var f0 = (double)i / this.Bands;// i/8
var f1 = (double)(i + 1) / this.Bands;// (i+1)/8
var y0 = this.Height * f0; //条带底部高度
var y1 = this.Height * f1;//条带顶部高度
var d0 = (this.BaseDiameter * (1 - f0)) + (this.TopDiameter * f0);//计算条带底部直径
var d1 = (this.BaseDiameter * (1 - f1)) + (this.TopDiameter * f1);//计算条带顶部直径
var builder = (this.Bands - i) % 2 == 1 ? redbuilder : whitebuilder;//交替创建红色和白色条带
builder.AddCone(this.Position + new Vector3D(0, 0, y0), new Vector3D(0, 0, 1), d0 / 2, d1 / 2, y1 - y0, i == 0, i == this.Bands - 1, 20);//添加新的红色或者白色圆锥台
}
//更新烟囱顶部中心位置
this.TopPosition = this.Position + new Vector3D(0, 0, this.Height);
this.redbands.Geometry = redbuilder.ToMesh(true);//红色圆锥台组 网格化。
this.whitebands.Geometry = whitebuilder.ToMesh(true);//设置白色条带集的几何体 : 白色圆锥台组网格化。
}
}
-->
<!--篱笆3D: 创建标杆和围栏网格。
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
using PropertyTools.DataAnnotations;//属性工具-数据注释
using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;
//继承自UI元素3D。
public class FenceVisual3D : UIElement3D
{ // 标杆直径 注册依赖属性
public static readonly DependencyProperty DiameterProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("Diameter", 0.05, (s, e) => s.AppearanceChanged());
//标杆高度 注册依赖属性:调用外观改变函数
public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("Height", 1, (s, e) => s.AppearanceChanged());
// 围栏位置 注册依赖属性
public static readonly DependencyProperty PositionsProperty = DependencyPropertyEx.Register<Point3DCollection, FenceVisual3D>("Positions", new Point3DCollection(), (s, e) => s.AppearanceChanged());
//网格大小 注册依赖属性
public static readonly DependencyProperty MeshSizeProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("MeshSize", 0.06, (s, e) => s.AppearanceChanged());
//标杆间距 注册依赖属性
public static readonly DependencyProperty PoleDistanceProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("PoleDistance", 2, (s, e) => s.AppearanceChanged());
//篱笆纹理 依赖属性
public static readonly DependencyProperty FenceTextureProperty = DependencyPropertyEx.Register<Brush, FenceVisual3D>("FenceTexture", null, (s, e) => s.FenceTextureChanged());
//标杆纹理属性
public static readonly DependencyProperty PoleTextureProperty = DependencyPropertyEx.Register<Brush, FenceVisual3D>("PoleTexture", null, (s, e) => s.PoleTextureChanged());
private readonly GeometryModel3D postsModel = new GeometryModel3D();//所有标杆几何体
private readonly GeometryModel3D fenceModel = new GeometryModel3D();
public FenceVisual3D()
{
this.AppearanceChanged();
var group = new Model3DGroup();
group.Children.Add(this.postsModel);//所有标杆几何体
group.Children.Add(this.fenceModel);
this.Visual3DModel = group;
}
//标杆直径属性,可浏览属性 0.01-2
[Category("Fence properties")]
[Slidable(0.01, 0.2)]
[FormatString("0.00")]
[Browsable(true)]
public double Diameter
{
get { return (double)this.GetValue(DiameterProperty); }
set { this.SetValue(DiameterProperty, value); }
}
//高度属性 可浏览,0-2
[Slidable(0, 2)]
[FormatString("0.00")]
[Browsable(true)]
public double Height
{
get { return (double)this.GetValue(HeightProperty); }
set { this.SetValue(HeightProperty, value); }
}
//围栏位置属性: 3D点集。 一个围栏可以由很多标杆。
public Point3DCollection Positions
{
get { return (Point3DCollection)this.GetValue(PositionsProperty); }
set { this.SetValue(PositionsProperty, value); }
}
//网格尺寸属性,0.01-2 ,可浏览
[Slidable(0.01, 0.2)]
[FormatString("0.00")]
[Browsable(true)]
public double MeshSize
{
get { return (double)this.GetValue(MeshSizeProperty); }
set { this.SetValue(MeshSizeProperty, value); }
}
//杆距离,0.2-10.可浏览
[Slidable(0.2, 10)]
[FormatString("0.00")]
[Browsable(true)]
public double PoleDistance
{
get { return (double)this.GetValue(PoleDistanceProperty); }
set { this.SetValue(PoleDistanceProperty, value); }
}
//篱笆纹理属性
public Brush FenceTexture
{
get { return (Brush)this.GetValue(FenceTextureProperty); }
set { this.SetValue(FenceTextureProperty, value); }
}
//标杆纹理属性
public Brush PoleTexture
{
get { return (Brush)this.GetValue(PoleTextureProperty); }
set { this.SetValue(PoleTextureProperty, value); }
}
//篱笆纹理改变 白色
private void FenceTextureChanged()
{
this.fenceModel.Material = this.fenceModel.BackMaterial = this.FenceTexture != null ?
new DiffuseMaterial(this.FenceTexture) { AmbientColor = Colors.White } : null;
}
//标杆纹理改变
private void PoleTextureChanged()
{
this.postsModel.Material = this.postsModel.BackMaterial = this.PoleTexture != null ? MaterialHelper.CreateMaterial(this.PoleTexture) : null;
}
//外观改变
private void AppearanceChanged()
{
var builder = new MeshBuilder(false, false);//标杆网格构建器
foreach (var p1 in DistributePoles(this.Positions, this.PoleDistance))//遍历标杆位置
{
var p2 = p1 + new Vector3D(0, 0, this.Height);
builder.AddCylinder(p1, p2, this.Diameter, 36);//绘制标杆
}
this.postsModel.Geometry = builder.ToMesh();//所有标杆几何体
var fenceBuilder = new MeshBuilder(false, true);//围栏网格 构建器
var w0 = 0d;
for (int i = 0; i + 1 < this.Positions.Count; i++)//遍历围栏位置
{ //围栏网格 的 四个顶点
var p0 = this.Positions[i];
var p1 = this.Positions[i + 1];
var p2 = this.Positions[i + 1] + new Vector3D(0, 0, this.Height);
var p3 = this.Positions[i] + new Vector3D(0, 0, this.Height);
var h = this.Height / this.MeshSize;//围栏网格的单元网格尺寸
var dw = p0.DistanceTo(p1) / this.MeshSize;
fenceBuilder.AddQuad(
p0,
p1,
p2,
p3,
new Point(w0, h),
new Point(w0 + dw, h),
new Point(w0 + dw, 0),
new Point(w0, 0));
w0 += dw;
}
this.fenceModel.Geometry = fenceBuilder.ToMesh();
}
//离散标杆:得到标杆位置点集合
private static IEnumerable<Point3D> DistributePoles(IList<Point3D> positions, double distance)
{
var l0 = 0d;
var x = distance;
for (int i = 0; i + 1 < positions.Count; i++)
{
var p0 = positions[i];
var p1 = positions[i + 1];
var d = p0.DistanceTo(p1);//围栏位置间距
while (x >= l0 && x < l0 + d)//离散化
{
var f = (x - l0) / d;
yield return p0 + ((p1 - p0) * f);//计算 标杆位置
x += distance;
}
l0 += d;
if (i + 1 == positions.Count)
{
yield return p1;//最后一个标杆位置
}
}
}
}
-->
<!-- 厂房3D:
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
using PropertyTools.DataAnnotations;
using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;
public class HouseVisual3D : UIElement3D
{ //依赖属性:房顶角度 默认15
public static readonly DependencyProperty RoofAngleProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("RoofAngle", 15, (s, e) => s.AppearanceChanged());
//依赖属性:房顶厚度 默认0.2
public static readonly DependencyProperty RoofThicknessProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("RoofThickness", 0.2, (s, e) => s.AppearanceChanged());
//依赖属性:地面厚度 默认0.2
public static readonly DependencyProperty FloorThicknessProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("FloorThickness", 0.2, (s, e) => s.AppearanceChanged());
//依赖属性:仓库高度 默认2.5
public static readonly DependencyProperty StoryHeightProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("StoryHeight", 2.5, (s, e) => s.AppearanceChanged());
//依赖属性:宽度 默认10
public static readonly DependencyProperty WidthProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("Width", 10, (s, e) => s.AppearanceChanged());
//依赖属性:长度 默认20
public static readonly DependencyProperty LengthProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("Length", 20, (s, e) => s.AppearanceChanged());
//依赖属性:楼层属性:默认1
public static readonly DependencyProperty StoriesProperty = DependencyPropertyEx.Register<int, HouseVisual3D>("Stories", 1, (s, e) => s.AppearanceChanged());
//依赖属性:爆炸屋顶属性,默认false
public static readonly DependencyProperty ExplodedRoofProperty = DependencyPropertyEx.Register<bool, HouseVisual3D>("ExplodedRoof", false, (s, e) => s.ExplodedRoofChanged());
private readonly GeometryModel3D roof = new GeometryModel3D();//房顶几何模型
private readonly GeometryModel3D walls = new GeometryModel3D();//墙几何模型
public HouseVisual3D()
{
this.roof.Material = MaterialHelper.CreateMaterial(Brushes.Brown, ambient: 10);//房顶材质-棕色
this.walls.Material = MaterialHelper.CreateMaterial(Brushes.White, ambient: 10);//墙材质 --白色
this.AppearanceChanged();//更新外观
var model = new Model3DGroup();
model.Children.Add(this.roof);//房顶
model.Children.Add(this.walls);//墙
this.Visual3DModel = model;//设置可见模型
}
//厂房宽度属性
[Category("House properties")]
[Slidable(0, 60)]
[Browsable(true)]
public double Width
{
get { return (double)this.GetValue(WidthProperty); }
set { this.SetValue(WidthProperty, value); }
}
//厂房长度属性
[Slidable(0, 60)]
[Browsable(true)]
public double Length
{
get { return (double)this.GetValue(LengthProperty); }
set { this.SetValue(LengthProperty, value); }
}
//楼层高度
[Slidable(0, 4)]
[FormatString("0.00")]
[Browsable(true)]
public double StoryHeight
{
get { return (double)this.GetValue(StoryHeightProperty); }
set { this.SetValue(StoryHeightProperty, value); }
}
//楼层数
[Slidable(1, 8)]
[Browsable(true)]
public int Stories
{
get { return (int)this.GetValue(StoriesProperty); }
set { this.SetValue(StoriesProperty, value); }
}
//房顶角度
[Slidable(0, 60)]
[Browsable(true)]
public double RoofAngle
{
get { return (double)this.GetValue(RoofAngleProperty); }
set { this.SetValue(RoofAngleProperty, value); }
}
//房顶厚度
[Slidable(0, 2)]
[FormatString("0.00")]
[Browsable(true)]
public double RoofThickness
{
get { return (double)this.GetValue(RoofThicknessProperty); }
set { this.SetValue(RoofThicknessProperty, value); }
}
//地面厚度
[Slidable(0, 1)]
[FormatString("0.00")]
[Browsable(true)]
public double FloorThickness
{
get { return (double)this.GetValue(FloorThicknessProperty); }
set { this.SetValue(FloorThicknessProperty, value); }
}
//爆炸房顶
[Browsable(true)]
public bool ExplodedRoof
{
get { return (bool)this.GetValue(ExplodedRoofProperty); }
set { this.SetValue(ExplodedRoofProperty, value); }
}
//爆炸房顶改变:
private void ExplodedRoofChanged()
{
if (ExplodedRoof)
roof.Transform = new TranslateTransform3D(0, 0, 30);//房顶沿Z轴平移30. 提高房顶。
else
roof.Transform = Transform3D.Identity;//房顶位置不变
}
//外观改变
private void AppearanceChanged()
{
var y0 = 0d;
var wallBuilder = new MeshBuilder(false, false);//墙构建器
for (int i = 0; i < this.Stories; i++)//遍历楼层
{
if (i > 0 && this.FloorThickness > 0)//楼层索引>0 且 地面厚度>0
{ //盒子参数:中心位置,长,宽,高
wallBuilder.AddBox(new Point3D(0, 0, y0 + this.FloorThickness / 2), this.Length + 0.2, this.Width + 0.2, this.FloorThickness);//添加盒子
y0 += this.FloorThickness;//更新高度
}
wallBuilder.AddBox(new Point3D(0, 0, y0 + this.StoryHeight / 2), this.Length, this.Width, this.StoryHeight);//绘制墙体盒子
y0 += this.StoryHeight;//更新高度
}
var theta = Math.PI / 180 * this.RoofAngle;//房顶倾角-弧度
var roofBuilder = new MeshBuilder(false, false);
var y1 = y0 + Math.Tan(theta) * this.Width / 2;
var p0 = new Point(0, y1);
var p1 = new Point(this.Width / 2 + 0.2 * Math.Cos(theta), y0 - 0.2 * Math.Sin(theta));
var p2 = new Point(p1.X + this.RoofThickness * Math.Sin(theta), p1.Y + this.RoofThickness * Math.Cos(theta));
var p3 = new Point(0, y1 + this.RoofThickness / Math.Cos(theta));
var p4 = new Point(-p2.X, p2.Y);
var p5 = new Point(-p1.X, p1.Y);
var roofSection = new[] { p0, p1, p1, p2, p2, p3, p3, p4, p4, p5, p5, p0 };//房顶的六个3D线段
roofBuilder.AddExtrudedSegments(roofSection, new Vector3D(0, -1, 0), new Point3D(-this.Length / 2, 0, 0), new Point3D(this.Length / 2, 0, 0));//添加指定3d线段集合的挤出面。
//绘制房顶棱线
var cap = new[] { p0, p1, p2, p3, p4, p5 };
roofBuilder.AddPolygon(cap, new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), new Point3D(-this.Length / 2, 0, 0));
roofBuilder.AddPolygon(cap, new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), new Point3D(this.Length / 2, 0, 0));
var p6 = new Point(this.Width / 2, y0);
var p7 = new Point(-this.Width / 2, y0);
//两三角形
wallBuilder.AddPolygon(new[] { p0, p6, p7 }, new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), new Point3D(-this.Length / 2, 0, 0));
wallBuilder.AddPolygon(new[] { p0, p6, p7 }, new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), new Point3D(this.Length / 2, 0, 0));
this.walls.Geometry = wallBuilder.ToMesh(true);
this.roof.Geometry = roofBuilder.ToMesh(true);
}
}
-->
<!-- 路边3d:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
using PropertyTools.DataAnnotations;
using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;
public class KerbVisual3D : UIElement3D
{ //宽度0.2,高度0.1,位置点集,长度1,纹理。 注册依赖属性,更新外观。
public static readonly DependencyProperty WidthProperty = DependencyPropertyEx.Register<double, KerbVisual3D>("Width", 0.2, (s, e) => s.AppearanceChanged());
public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, KerbVisual3D>("Height", 0.1, (s, e) => s.AppearanceChanged());
public static readonly DependencyProperty PositionsProperty = DependencyPropertyEx.Register<Point3DCollection, KerbVisual3D>("Positions", new Point3DCollection(), (s, e) => s.AppearanceChanged());
public static readonly DependencyProperty LengthProperty = DependencyPropertyEx.Register<double, KerbVisual3D>("Length", 1, (s, e) => s.AppearanceChanged());
public static readonly DependencyProperty TextureProperty = DependencyPropertyEx.Register<Brush, KerbVisual3D>("Texture", null, (s, e) => s.TextureChanged());
private readonly GeometryModel3D kerbModel = new GeometryModel3D();//路边几何模型
public KerbVisual3D()
{
this.AppearanceChanged();//更新外观
this.Visual3DModel = this.kerbModel;//设置几何模型
}
//宽度
[Category("Kerb properties")]
[Slidable(0.1, 1)]
[FormatString("0.00")]
[Browsable(true)]
public double Width
{
get { return (double)this.GetValue(WidthProperty); }
set { this.SetValue(WidthProperty, value); }
}
//高度
[Slidable(0, 2)]
[FormatString("0.00")]
[Browsable(true)]
public double Height
{
get { return (double)this.GetValue(HeightProperty); }
set { this.SetValue(HeightProperty, value); }
}
//位置点集
public Point3DCollection Positions
{
get { return (Point3DCollection)this.GetValue(PositionsProperty); }
set { this.SetValue(PositionsProperty, value); }
}
//长度
[Slidable(0.1, 10)]
[FormatString("0.00")]
[Browsable(true)]
public double Length
{
get { return (double)this.GetValue(LengthProperty); }
set { this.SetValue(LengthProperty, value); }
}
//纹理
public Brush Texture
{
get { return (Brush)this.GetValue(TextureProperty); }
set { this.SetValue(TextureProperty, value); }
}
//路边材质和背面材质
private void TextureChanged()
{
this.kerbModel.Material = this.Texture != null ? new DiffuseMaterial(this.Texture) : null;
this.kerbModel.BackMaterial = this.Texture != null ? Materials.Gray : null;
}
//更新外观
private void AppearanceChanged()
{
var builder = new MeshBuilder(false, true);
//硬编码路边部分 hard code a kerb section
var section = new PointCollection();
int m = 41;//修改m,n 改变路边尺寸
double n = 4;//
double a = this.Width / 2;
double b = this.Height;
for (int i = 0; i < m; i++)
{
double t = Math.PI * i / (m - 1);
section.Add(new Point(
a * Math.Sign(Math.Cos(t)) * Math.Pow(Math.Abs(Math.Cos(t)), 2 / n),
-b * Math.Sign(Math.Sin(t)) * Math.Pow(Math.Abs(Math.Sin(t)), 2 / n)));
}
//计算纹理坐标 calculate the texture coordinates
var values = new List<double> { 0 };
for (int i = 1; i < this.Positions.Count; i++)
{
var d = this.Positions[i - 1].DistanceTo(this.Positions[i]);
values.Add(values[values.Count - 1] + (d / this.Length));
}
// create the extruded geometry创建拉伸几何体
builder.AddTube(this.Positions, null, values, null, section, new Vector3D(1, 0, 0), false, false);
this.kerbModel.Geometry = builder.ToMesh();//设置几何体
}
}
-->
<!-- 筒仓3D:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
using PropertyTools.DataAnnotations;
using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;
public class SiloVisual3D : UIElement3D
{ //直径属性,高度20,圆顶高度,圆顶直径。
public static readonly DependencyProperty DiameterProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("Diameter", 40, (s, e) => s.AppearanceChanged());
public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("Height", 20, (s, e) => s.AppearanceChanged());
public static readonly DependencyProperty DomeHeightProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("DomeHeight", 8, (s, e) => s.AppearanceChanged());
public static readonly DependencyProperty DomeDiameterProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("DomeDiameter", 38, (s, e) => s.AppearanceChanged());
private readonly GeometryModel3D walls = new GeometryModel3D();//围墙
private readonly GeometryModel3D railing = new GeometryModel3D();//栏杆
private readonly GeometryModel3D stairs = new GeometryModel3D();//楼梯
public SiloVisual3D()
{
this.walls.Material = MaterialHelper.CreateMaterial(Brushes.White, ambient: 10);
this.walls.BackMaterial = MaterialHelper.CreateMaterial(Brushes.Green);
this.railing.Material = MaterialHelper.CreateMaterial(Brushes.Silver, ambient: 10);
this.stairs.Material = MaterialHelper.CreateMaterial(Brushes.Brown, ambient: 10);
this.AppearanceChanged();
var group = new Model3DGroup();
group.Children.Add(this.walls);
group.Children.Add(this.railing);
group.Children.Add(this.stairs);
this.Visual3DModel = group;
}
//直径
[Category("Silo/tank properties")]
[Slidable(0, 100)]
[FormatString("0.00")]
[Browsable(true)]
public double Diameter
{
get { return (double)this.GetValue(DiameterProperty); }
set { this.SetValue(DiameterProperty, value); }
}
//高度
[Slidable(0, 100)]
[FormatString("0.00")]
[Browsable(true)]
public double Height
{
get { return (double)this.GetValue(HeightProperty); }
set { this.SetValue(HeightProperty, value); }
}
//圆顶高度
[Slidable(0, 20)]
[FormatString("0.00")]
[Browsable(true)]
public double DomeHeight
{
get { return (double)this.GetValue(DomeHeightProperty); }
set { this.SetValue(DomeHeightProperty, value); }
}
//圆顶直径
[Slidable(0, 50)]
[FormatString("0.00")]
[Browsable(true)]
public double DomeDiameter
{
get { return (double)this.GetValue(DomeDiameterProperty); }
set { this.SetValue(DomeDiameterProperty, value); }
}
//更新外观
private void AppearanceChanged()
{
var builder = new MeshBuilder(false, false);
var p0 = new Point(0, 0);
var p1 = new Point(this.Diameter / 2, 0);
var p2 = new Point(this.Diameter / 2, this.Height);
var p3 = new Point(this.DomeDiameter / 2, this.Height);
var section = new List<Point> { p0, p1, p1, p2, p2, p3 };
var sectionIndices = new List<int> { 0, 1, 2, 3, 4, 5 };
int n = 40;
for (int i = n; i >= 0; i--)
{
double x = (double)i / n;
double y = x * x;
if (i < n)
{
sectionIndices.Add(section.Count - 1);
sectionIndices.Add(section.Count);
}
section.Add(new Point(x * this.DomeDiameter / 2, this.Height + (this.DomeHeight * (1 - y))));
}
builder.AddSurfaceOfRevolution(new Point3D(0, 0, 0), new Vector3D(0, 0, 1), section, sectionIndices, 80);//围墙
this.walls.Geometry = builder.ToMesh(true);
var treadDepth = 0.3;
var riseHeight = 0.15;
var thickness = 0.05;
var width = 1;
var steps = (int)(this.Height / riseHeight);
var r = (this.Diameter * 0.5) + (width * 0.5);
var rp = (this.Diameter * 0.5) + (width * 0.95);
var stairBuilder = new MeshBuilder(false, false);
var railBases = new List<Point3D>();
for (int i = 0; i < steps; i++)
{
var theta = treadDepth * i / r;
var p = new Point3D(Math.Cos(theta) * r, Math.Sin(theta) * r, (riseHeight * i) + (thickness / 2));
var x = new Vector3D(Math.Cos(theta), Math.Sin(theta), 0);
var z = new Vector3D(0, 0, 1);
var y = Vector3D.CrossProduct(z, x);
stairBuilder.AddBox(p, x, y, width, treadDepth, thickness);//楼梯
railBases.Add(new Point3D(Math.Cos(theta) * rp, Math.Sin(theta) * rp, (riseHeight * i) + thickness));//楼梯围栏
}
var lastTheta = treadDepth * steps / r;
// Railing along stairs
var railingHeight = 0.8;
var railingDiameter = 0.05;
var railings = 3;
var railingBuilder = new MeshBuilder(false, false);
// 顶部栏杆Top railing
var railingPostDistance = 0.5;
int topRailingPosts = (int)(this.Diameter * Math.PI / railingPostDistance);
var tr = (this.Diameter / 2) - railingDiameter;
for (int i = 0; i < topRailingPosts; i++)
{
var theta = lastTheta + (2 * Math.PI * i / topRailingPosts);
railBases.Add(new Point3D(Math.Cos(theta) * tr, Math.Sin(theta) * tr, this.Height));
}
BuildRailing(railingBuilder, railBases, railingHeight, railingDiameter, railings);
this.stairs.Geometry = stairBuilder.ToMesh();
this.railing.Geometry = railingBuilder.ToMesh();
}
//构建栏杆
private static void BuildRailing(
MeshBuilder railingBuilder,
List<Point3D> bases,
double height,
double diameter,
int railings)
{
foreach (var point in bases)
{
railingBuilder.AddCylinder(point, point + new Vector3D(0, 0, height), diameter, 10);
}
for (int i = 1; i <= railings; i++)
{
var h = height * i / railings;
var path = bases.Select(p => p + new Vector3D(0, 0, h)).ToArray();
railingBuilder.AddTube(path, diameter, 10, false);
}
}
}
-->
<!-- 超椭圆球 为 XAML 标记扩展提供基类。
using System;
using System.Windows.Markup;
using System.Windows.Media.Media3D;
public class Superellipse : MarkupExtension
{
private readonly double a;
private readonly double b;
private readonly double n;
public Superellipse(double a, double b, double n)
{
this.a = a;
this.b = b;
this.n = n;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var c = new Point3DCollection();
int m = 400;
for (int i = 0; i + 1 < m; i++)
{
double t = 2 * Math.PI * i / (m - 2);
c.Add(new Point3D(
this.a * Math.Sign(Math.Cos(t)) * Math.Pow(Math.Abs(Math.Cos(t)), 2 / this.n),
this.b * Math.Sign(Math.Sin(t)) * Math.Pow(Math.Abs(Math.Sin(t)), 2 / this.n),
0));
}
c.Add(c[0]);
return c;
}
}
-->
<!-- 视图模型:可观察
using System.Windows.Media.Media3D;
using PropertyTools;//属性工具
public class ViewModel : Observable
{
private object selectedObject;//选择的对象
// 获取或设置选择的对象
public object SelectedObject
{
get
{
return this.selectedObject;
}
set
{
this.SetValue(ref this.selectedObject, value, nameof(this.SelectedObject));
}
}
//选择对象
public void Select(Visual3D visual)
{
this.SelectedObject = visual;
}
}
-->