첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형

TestProject.zip
다운로드

▶ MainWindow.xaml

<Window x:Class="TestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="800"
    Height="600"
    Title="3차원 멩거 스폰지 프랙탈(Menger Sponge Fractal) 만들기"
    Loaded="Window_Loaded"
    PreviewKeyDown="Window_KeyDown">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="*"  />
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal">
            <Label Content="깊이 :" />
            <ComboBox Name="depthComboBox"
                Width="50"
                SelectionChanged="depthComboBox_SelectionChanged">
                <Label Content="1" />
                <Label Content="2" />
                <Label Content="3" />
                <Label Content="4" />
                <Label Content="5" />
            </ComboBox>
        </StackPanel>
        <Viewport3D Name="viewport3D" Grid.Row="1"
            IsHitTestVisible="False" />
    </Grid>
</Window>

 

728x90

 

▶ MainWindow.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 스폰지 깊이
        /// </summary>
        private int spongeDepth = 3;

        /// <summary>
        /// 카메라
        /// </summary>
        private PerspectiveCamera camera;

        /// <summary>
        /// 카메라 파이
        /// </summary>
        private double cameraPhi = Math.PI / 6.0;

        /// <summary>
        /// 카메라 세타
        /// </summary>
        private double cameraTheta = Math.PI / 6.0;

        /// <summary>
        /// 카메라 R
        /// </summary>
        private double cameraR = 7.0;

        /// <summary>
        /// 카메라 델타 파이
        /// </summary>
        private const double CAMERA_DELTA_PHI = 0.1;

        /// <summary>
        /// 카메라 델타 세타
        /// </summary>
        private const double CAMERA_DELTA_THETA = 0.1;

        /// <summary>
        /// 카메라 델타 R
        /// </summary>
        private const double CAMERA_DELTA_R = 0.5;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)

        /// <summary>
        /// 윈도우 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.depthComboBox.SelectedIndex = 2;

            this.camera = new PerspectiveCamera();

            this.camera.FieldOfView = 60;

            this.viewport3D.Camera = this.camera;

            SetCameraPosition();

            PrepareModel();
        }

        #endregion
        #region 윈도우 키 DOWN 처리하기 - Window_KeyDown(sender, e)

        /// <summary>
        /// 윈도우 키 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            switch(e.Key)
            {
                case Key.Up :

                    this.cameraPhi += CAMERA_DELTA_PHI;

                    if(this.cameraPhi > Math.PI / 2.0)
                    {
                        this.cameraPhi = Math.PI / 2.0;
                    }

                    e.Handled = true;

                    break;

                case Key.Down :

                    this.cameraPhi -= CAMERA_DELTA_PHI;

                    if(this.cameraPhi < -Math.PI / 2.0)
                    {
                        this.cameraPhi = -Math.PI / 2.0;
                    }

                    e.Handled = true;

                    break;

                case Key.Left :

                    this.cameraTheta += CAMERA_DELTA_THETA;

                    e.Handled = true;

                    break;

                case Key.Right :

                    this.cameraTheta -= CAMERA_DELTA_THETA;

                    e.Handled = true;

                    break;

                case Key.Add     :
                case Key.OemPlus :

                    this.cameraR -= CAMERA_DELTA_R;

                    if(this.cameraR < CAMERA_DELTA_R)
                    {
                        this.cameraR = CAMERA_DELTA_R;
                    }

                    e.Handled = true;

                    break;

                case Key.Subtract :
                case Key.OemMinus :

                    this.cameraR += CAMERA_DELTA_R;

                    e.Handled = true;

                    break;
            }

            SetCameraPosition();
        }

        #endregion
        #region 깊이 콤보 박스 선택 변경시 처리하기 - depthComboBox_SelectionChanged(sender, e)

        /// <summary>
        /// 깊이 콤보 박스 선택 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void depthComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if(this.viewport3D == null)
            {
                return;
            }

            Label label = this.depthComboBox.SelectedItem as Label;

            int newDepth = int.Parse(label.Content.ToString());

            if(newDepth == this.spongeDepth)
            {
                return;
            }

            this.spongeDepth = newDepth;

            PrepareModel();

            this.viewport3D.Focus();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 카메라 위치 설정하기 - SetCameraPosition()

        /// <summary>
        /// 카메라 위치 설정하기
        /// </summary>
        private void SetCameraPosition()
        {
            double y   = this.cameraR * Math.Sin(this.cameraPhi);
            double hyp = this.cameraR * Math.Cos(this.cameraPhi);
            double x   = hyp * Math.Cos(this.cameraTheta);
            double z   = hyp * Math.Sin(this.cameraTheta);

            this.camera.Position = new Point3D(x, y, z);

            this.camera.LookDirection = new Vector3D(-x, -y, -z);

            this.camera.UpDirection = new Vector3D(0, 1, 0);
        }

        #endregion
        #region 조명 정의하기 - DefineLight(model3DGroup)

        /// <summary>
        /// 조명 정의하기
        /// </summary>
        /// <param name="model3DGroup">3차원 모델 그룹</param>
        private void DefineLight(Model3DGroup model3DGroup)
        {
            model3DGroup.Children.Add(new AmbientLight(Colors.DarkSlateGray));
            model3DGroup.Children.Add(new DirectionalLight(Colors.Gray, new Vector3D(3.0, -2.0, 1.0)));
            model3DGroup.Children.Add(new DirectionalLight(Colors.DarkGray, new Vector3D(-3.0, -2.0, -1.0)));
        }

        #endregion
        #region 포인트 추가하기 - AddPoint(point3DCollection, point3D)

        /// <summary>
        /// 포인트 추가하기
        /// </summary>
        /// <param name="point3DCollection">3차원 포인트 컬렉션</param>
        /// <param name="point3D">3차원 포인트</param>
        /// <returns>포인트 인덱스</returns>
        private int AddPoint(Point3DCollection point3DCollection, Point3D point3D)
        {
            point3DCollection.Add(point3D);

            return point3DCollection.Count - 1;
        }

        #endregion
        #region 삼각형 추가하기 - AddTriangle(meshGeometry3D, point3D1, point3D2, point3D3)

        /// <summary>
        /// 삼각형 추가하기
        /// </summary>
        /// <param name="meshGeometry3D">3차원 메쉬 기하</param>
        /// <param name="point3D1">3차원 포인트 1</param>
        /// <param name="point3D2">3차원 포인트 2</param>
        /// <param name="point3D3">3차원 포인트 3</param>
        private void AddTriangle(MeshGeometry3D meshGeometry3D, Point3D point3D1, Point3D point3D2, Point3D point3D3)
        {
            int index1 = AddPoint(meshGeometry3D.Positions, point3D1);
            int index2 = AddPoint(meshGeometry3D.Positions, point3D2);
            int index3 = AddPoint(meshGeometry3D.Positions, point3D3);

            meshGeometry3D.TriangleIndices.Add(index1);
            meshGeometry3D.TriangleIndices.Add(index2);
            meshGeometry3D.TriangleIndices.Add(index3);
        }

        #endregion
        #region 큐브 추가하기 - AddCube(meshGeometry3D, xMinimum, xMaximum, yMinimum, yMaximum, zMinimum, zMaximum)

        /// <summary>
        /// 큐브 추가하기
        /// </summary>
        /// <param name="meshGeometry3D">3차원 메쉬 기하</param>
        /// <param name="xMinimum">X 최소값</param>
        /// <param name="xMaximum">X 최대값</param>
        /// <param name="yMinimum">Y 최소값</param>
        /// <param name="yMaximum">Y 최대값</param>
        /// <param name="zMinimum">Z 최소값</param>
        /// <param name="zMaximum">Z 최대값</param>
        private void AddCube(MeshGeometry3D meshGeometry3D, double xMinimum, double xMaximum, double yMinimum, double yMaximum,
            double zMinimum, double zMaximum)
        {
            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMaximum, zMaximum),
                new Point3D(xMaximum, yMaximum, zMaximum),
                new Point3D(xMaximum, yMaximum, zMinimum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMaximum, zMaximum),
                new Point3D(xMaximum, yMaximum, zMinimum),
                new Point3D(xMinimum, yMaximum, zMinimum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMinimum, zMaximum),
                new Point3D(xMinimum, yMinimum, zMinimum),
                new Point3D(xMaximum, yMinimum, zMinimum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMinimum, zMaximum),
                new Point3D(xMaximum, yMinimum, zMinimum),
                new Point3D(xMaximum, yMinimum, zMaximum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMaximum, zMaximum),
                new Point3D(xMinimum, yMaximum, zMinimum),
                new Point3D(xMinimum, yMinimum, zMinimum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMaximum, zMaximum),
                new Point3D(xMinimum, yMinimum, zMinimum),
                new Point3D(xMinimum, yMinimum, zMaximum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMaximum, yMaximum, zMaximum),
                new Point3D(xMaximum, yMinimum, zMaximum),
                new Point3D(xMaximum, yMinimum, zMinimum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMaximum, yMaximum, zMaximum),
                new Point3D(xMaximum, yMinimum, zMinimum),
                new Point3D(xMaximum, yMaximum, zMinimum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMaximum, zMaximum),
                new Point3D(xMinimum, yMinimum, zMaximum),
                new Point3D(xMaximum, yMinimum, zMaximum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMaximum, zMaximum),
                new Point3D(xMaximum, yMinimum, zMaximum),
                new Point3D(xMaximum, yMaximum, zMaximum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMinimum, zMinimum),
                new Point3D(xMaximum, yMinimum, zMinimum),
                new Point3D(xMaximum, yMaximum, zMinimum)
            );

            AddTriangle
            (
                meshGeometry3D,
                new Point3D(xMinimum, yMinimum, zMinimum),
                new Point3D(xMaximum, yMaximum, zMinimum),
                new Point3D(xMinimum, yMaximum, zMinimum)
            );
        }

        #endregion
        #region 멩거 스폰지 프랙탈 만들기 - MakeMengerSpongeFractal(meshGeometry3D, xMinimum, xMaximum, yMinimum, yMaximum, zMinimum, zMaximum, depth)

        /// <summary>
        /// 멩거 스폰지 프랙탈 만들기
        /// </summary>
        /// <param name="meshGeometry3D">3차원 메쉬 기하</param>
        /// <param name="xMinimum">X 최소값</param>
        /// <param name="xMaximum">X 최대값</param>
        /// <param name="yMinimum">Y 최소값</param>
        /// <param name="yMaximum">Y 최대값</param>
        /// <param name="zMinimum">z 최소값</param>
        /// <param name="zMaximum">z 최대값</param>
        /// <param name="depth">깊이</param>
        private void MakeMengerSpongeFractal(MeshGeometry3D meshGeometry3D, double xMinimum, double xMaximum, double yMinimum, double yMaximum,
            double zMinimum, double zMaximum, int depth)
        {
            if(depth == 1)
            {
                AddCube(meshGeometry3D, xMinimum, xMaximum, yMinimum, yMaximum, zMinimum, zMaximum);
            }
            else
            {
                double deltaX = (xMaximum - xMinimum) / 3.0;
                double deltaY = (yMaximum - yMinimum) / 3.0;
                double deltaZ = (zMaximum - zMinimum) / 3.0;

                for(int x = 0; x < 3; x++)
                {
                    for(int y = 0; y < 3; y++)
                    {
                        if((x == 1) && (y == 1))
                        {
                            continue;
                        }

                        for(int z = 0; z < 3; z++)
                        {
                            if((z == 1) && ((x == 1) || (y == 1)))
                            {
                                continue;
                            }

                            MakeMengerSpongeFractal
                            (
                                meshGeometry3D,
                                xMinimum + deltaX * x,
                                xMinimum + deltaX * (x + 1),
                                yMinimum + deltaY * y,
                                yMinimum + deltaY * (y + 1),
                                zMinimum + deltaZ * z,
                                zMinimum + deltaZ * (z + 1),
                                depth - 1
                            );
                        }
                    }
                }
            }
        }

        #endregion
        #region 모델 생성하기 - CreateModel(model3DGroup, depth)

        /// <summary>
        /// 모델 생성하기
        /// </summary>
        /// <param name="model3DGroup">3차원 모델 그룹</param>
        /// <param name="depth">깊이</param>
        private void CreateModel(Model3DGroup model3DGroup, int depth)
        {
            MeshGeometry3D meshGeometry3D = new MeshGeometry3D();

            MakeMengerSpongeFractal(meshGeometry3D, -2, 2, -2, 2, -2, 2, depth);

            DiffuseMaterial diffuseMaterial = new DiffuseMaterial(Brushes.Fuchsia);

            GeometryModel3D geometryModel3D = new GeometryModel3D(meshGeometry3D, diffuseMaterial);

            geometryModel3D.BackMaterial = diffuseMaterial;

            model3DGroup.Children.Add(geometryModel3D);
        }

        #endregion
        #region 모델 준비하기 - PrepareModel()

        /// <summary>
        /// 모델 준비하기
        /// </summary>
        /// <returns>3차원 모델 그룹</returns>
        private Model3DGroup PrepareModel()
        {
            Model3DGroup model3DGroup = new Model3DGroup();

            DefineLight(model3DGroup);

            CreateModel(model3DGroup, this.spongeDepth);

            ModelVisual3D modelVisual3D = new ModelVisual3D();

            modelVisual3D.Content = model3DGroup;

            viewport3D.Children.Clear();
            viewport3D.Children.Add(modelVisual3D);

            return model3DGroup;
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by 사용자 icodebroker
TAG , , ,

댓글을 달아 주세요