첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

■ 3차원 멩거 스폰지 프랙탈(Menger Sponge Fractal) 만들기

------------------------------------------------------------------------------------------------------------------------


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>

 

 

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

    }

}

 

------------------------------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker

댓글을 달아 주세요