WPF:3D正二十面体

一、坐标系

  • WPF 的 3D 坐标系原点通常位于对象中心,X 轴向右,Y 轴向上,Z 轴朝向观察者(右手定则)。
    正四面体旋转以原点为中心,绕X,Y,Z 轴自动旋转。

二、正二十面体的坐标

1. 正二十面体的几何特征

  • 顶点数:12
  • 面数:20,每个面都是正三角形
  • 边数:60
  • 对偶多面体:正十二面体(12个顶点 ↔ 12个面)

2. 顶点坐标的数学构造

  • 标准坐标: (±1, ±φ, 0), (0, ±1, ±φ), (±φ, 0, ±1) 其中φ=(1+√5)/2
    顶点坐标

0 = (-1, φ, 0)
1 = (1, φ, 0)
2 = (-1, -φ, 0)
3 = (1, -φ, 0)
4 = (0, -1, φ)
5 = (0, 1, φ)
6 = (0, -1, -φ)
7 = (0, 1, -φ)
8 = (φ, 0, -1)
9 = (φ, 0, 1)
10 = (-φ, 0, -1)
11 = (-φ, 0, 1)

  • 定义三角形面:每个面由三个顶点索引组成。
    正20面体有20个面,每个面3个顶点,共60个索引。
    三角形面:面:

{0, 11, 5}
{0, 5, 1}
{0, 1, 7}
{0, 7, 10}
{0, 10, 11}
{1, 5, 9}
{5, 11, 4}
{11, 10, 2}
{10, 7, 6}
{7, 1, 8}
{3, 9, 4}
{3, 4, 2}
{3, 2, 6}
{3, 6, 8}
{3, 8, 9}
{4, 9, 5}
{2, 4, 11}
{6, 2, 10}
{8, 6, 7}
{9, 8, 1}

0 个面包含 {0,11,5}、{0,5,1}、{0,1,7}、{0,7,10}、{0,10,11} -> 围绕顶点 0 的 5 个面(度数 5)。完美!
1 个面包含 {0,5,1}、{0,1,7}、{1,5,9}、{9,8,1}、{7,1,8} -> 围绕顶点 1 的 5 个面。完美!

  • 在 C# 代码中动态构建

三、MainWindow.xaml

<Window x:Class="WpfA_Icosahedron.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:local="clr-namespace:WpfA_Icosahedron"
        mc:Ignorable="d"
        Title="正二十面体" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="250"/>
        </Grid.ColumnDefinitions>

        <!-- 右侧控制面板 -->
        <StackPanel Grid.Column="1" Margin="10">
            <TextBlock Text="调节相机焦距" Margin="0,0,0,5"/>
            <Slider x:Name="viewAngle" Width="200" Value="10" Minimum="5" Maximum="100" />
            <TextBox Text="{Binding ElementName=viewAngle, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                     Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>

            <TextBlock Text="X轴旋转" Margin="0,0,0,5"/>
            <Slider x:Name="rotateX" Width="200" Value="0" Minimum="0" Maximum="360" />
            <TextBox Text="{Binding ElementName=rotateX, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                     Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>

            <TextBlock Text="Y轴旋转" Margin="0,0,0,5"/>
            <Slider x:Name="rotateY" Width="200" Value="0" Minimum="0" Maximum="360" />
            <TextBox Text="{Binding ElementName=rotateY, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                     Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>

            <TextBlock Text="Z轴旋转" Margin="0,0,0,5"/>
            <Slider x:Name="rotateZ" Width="200" Value="0" Minimum="0" Maximum="360" />
            <TextBox Text="{Binding ElementName=rotateZ, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                     Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>

            <Button x:Name="btnAutoRotate" Content="启动自动旋转" Width="120" Margin="0,20,0,5" Click="BtnAutoRotate_Click"/>
            <Button x:Name="btnExit" Content="退出程序" Width="120" Click="BtnExit_Click"/>
        </StackPanel>


        <Viewport3D Grid.Column="0">
            <!-- 模型容器,并应用旋转转换(绑定滑块) -->
          <ModelVisual3D x:Name="modelVisual">
         
                 <ModelVisual3D.Content>
                       <Model3DGroup>
                                <!-- 我们的正二十面体模型,几何数据在后台生成 -->
                                <GeometryModel3D x:Name="IcosahedronModel">
                                    <GeometryModel3D.Material>
                                        <DiffuseMaterial Brush="DodgerBlue" />
                                    </GeometryModel3D.Material>
                                    <GeometryModel3D.BackMaterial>
                                        <DiffuseMaterial Brush="DarkBlue" />
                                    </GeometryModel3D.BackMaterial>
                                </GeometryModel3D>

                                <!-- 光源 -->
                                <AmbientLight Color="#404040" />
                                <DirectionalLight Color="White" Direction="1, -3, -2" />
                                <DirectionalLight Color="Gray" Direction="-1, 2, 3" />
                        </Model3DGroup>
                  </ModelVisual3D.Content>
                

                <!-- 旋转变换(保持完全一致) -->
                   <ModelVisual3D.Transform>
                            <Transform3DGroup>
                                <RotateTransform3D>
                                    <RotateTransform3D.Rotation>
                                        <AxisAngleRotation3D Axis="1,0,0" Angle="{Binding Value, ElementName=rotateX}"/>
                                    </RotateTransform3D.Rotation>
                                </RotateTransform3D>
                                <RotateTransform3D>
                                    <RotateTransform3D.Rotation>
                                        <AxisAngleRotation3D Axis="0,1,0" Angle="{Binding Value, ElementName=rotateY}"/>
                                    </RotateTransform3D.Rotation>
                                </RotateTransform3D>
                                <RotateTransform3D>
                                    <RotateTransform3D.Rotation>
                                        <AxisAngleRotation3D Axis="0,0,1" Angle="{Binding Value, ElementName=rotateZ}"/>
                                    </RotateTransform3D.Rotation>
                                </RotateTransform3D>
                            </Transform3DGroup>
                    </ModelVisual3D.Transform>
           </ModelVisual3D>

                <Viewport3D.Camera>

                    <PerspectiveCamera Position="25,15,40" LookDirection="-25,-15,-40" 
                            FieldOfView="{Binding Value, ElementName=viewAngle}" 
                            UpDirection="0,1,0"/>
                    
                </Viewport3D.Camera>
               
        </Viewport3D>
    </Grid>
</Window>

四、MainWindow.xaml.cs

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Threading;

namespace WpfA_Icosahedron
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private DispatcherTimer? _rotationTimer;  // 可为 null
        private const double RotationStep = 0.5;   // 每帧旋转角度增量



        public MainWindow()
        {
            InitializeComponent();
            // 设置初始旋转角度,让模型有一个良好的立体视角
            rotateX.Value = 20;
            rotateY.Value = -30;
            rotateZ.Value = 0;
            BuildIcosahedron();
            // 初始化定时器
            _rotationTimer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(30) // 约 33 帧/秒
            };
            _rotationTimer.Tick += RotationTimer_Tick;


        }

        /// <summary>
        /// 构建正二十面体的3D模型
        /// </summary>
        private void BuildIcosahedron()
        {
            // 1. 黄金比例
            double phi = (1 + Math.Sqrt(5)) / 2; // ≈ 1.618

            // 2. 定义正二十面体的 12 个基础顶点
            //    坐标模式:(0, ±1, ±φ), (±1, ±φ, 0), (±φ, 0, ±1)
            Point3D[] baseVertices = new Point3D[]
            {
                new Point3D(-1,  phi, 0),    // 0
                new Point3D( 1,  phi, 0),    // 1
                new Point3D(-1, -phi, 0),    // 2
                new Point3D( 1, -phi, 0),    // 3
                new Point3D( 0,  -1,  phi),  // 4
                new Point3D( 0,   1,  phi),  // 5
                new Point3D( 0,  -1, -phi),  // 6
                new Point3D( 0,   1, -phi),  // 7
                new Point3D( phi, 0,  -1),   // 8
                new Point3D( phi, 0,   1),   // 9
                new Point3D(-phi, 0,  -1),   // 10
                new Point3D(-phi, 0,   1)    // 11
            };

            // 3. 定义 20 个三角形的顶点索引(每个三角形引用上面 12 个顶点中的 3 个)
            int[][] triangles = new int[][]
            {
                new int[]{0, 11, 5}, new int[]{0, 5, 1}, new int[]{0, 1, 7},
                new int[]{0, 7, 10}, new int[]{0, 10, 11}, new int[]{1, 5, 9},
                new int[]{5, 11, 4}, new int[]{11, 10, 2}, new int[]{10, 7, 6},
                new int[]{7, 1, 8}, new int[]{3, 9, 4}, new int[]{3, 4, 2},
                new int[]{3, 2, 6}, new int[]{3, 6, 8}, new int[]{3, 8, 9},
                new int[]{4, 9, 5}, new int[]{2, 4, 11}, new int[]{6, 2, 10},
                new int[]{8, 6, 7}, new int[]{9, 8, 1}
            };

            // 4. 构建网格(为了获得清晰的棱边效果,每个三角形使用独立的顶点,不共享)
            MeshGeometry3D mesh = new MeshGeometry3D();
            int index = 0;

            foreach (var tri in triangles)
            {
                // 依次添加三角形的三个顶点
                mesh.Positions.Add(baseVertices[tri[0]]);
                mesh.Positions.Add(baseVertices[tri[1]]);
                mesh.Positions.Add(baseVertices[tri[2]]);

                // 添加三个索引(因为是顺序添加,所以索引连续递增)
                mesh.TriangleIndices.Add(index++);
                mesh.TriangleIndices.Add(index++);
                mesh.TriangleIndices.Add(index++);
            }

              // 5. 将生成的网格赋值给 XAML 中定义的模型
            IcosahedronModel.Geometry = mesh;

           
        }


        /// <summary>
        /// 启动/停止自动旋转
        /// </summary>
         // 自动旋转按钮点击事件
        private void BtnAutoRotate_Click(object sender, RoutedEventArgs e)
        {
            if (_rotationTimer == null)
            {
                // 创建定时器,每 50ms 触发一次(约 20fps)
                _rotationTimer = new DispatcherTimer
                {
                    Interval = TimeSpan.FromMilliseconds(50)
                };
                _rotationTimer.Tick += RotationTimer_Tick;
                _rotationTimer.Start();
                btnAutoRotate.Content = "停止自动旋转";
            }
            else
            {
                _rotationTimer.Stop();
                _rotationTimer.Tick -= RotationTimer_Tick;
                _rotationTimer = null;
                btnAutoRotate.Content = "启动自动旋转";
            }


        }


        // 定时器 Tick:累加 Z 轴角度
        // 注意这里的 sender 参数类型为 object?,与 EventHandler 委托匹配
        private void RotationTimer_Tick(object? sender, EventArgs e)
        {
            // 三个角度每次增加 1 度(可调整速度)
            rotateX.Value = (rotateX.Value + 1) % 360;
            rotateY.Value = (rotateY.Value + 1) % 360;
            rotateZ.Value = (rotateZ.Value + 1) % 360;
        }

        /// <summary>
        /// 退出程序
        /// </summary>
        private void BtnExit_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容