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

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="12면체(Dodecahedron) 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16"
    Loaded="Window_Loaded"
    KeyDown="Window_KeyDown">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Viewport3D Name="viewport3D" Grid.Row="1" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="3" />
        <CheckBox Name="axisCheckBox" Grid.Row="0" Grid.Column="0"
            Margin="10"
            IsChecked="True"
            Content="축"
            Click="checkBox_Click" />
        <CheckBox Name="edgeCheckBox" Grid.Row="0" Grid.Column="1"
            Margin="10"
            IsChecked="True"
            Content="에지"
            Click="checkBox_Click" />
        <CheckBox Name="faceCheckBox" Grid.Row="0" Grid.Column="2"
            Margin="10"
            IsChecked="True"
            Content="면"
            Click="checkBox_Click"/>
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
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 Model3DGroup modelGroup = new Model3DGroup();

        /// <summary>
        /// 조명 리스트
        /// </summary>
        private List<Light> lightList = new List<Light>();

        /// <summary>
        /// 면 모델
        /// </summary>
        private GeometryModel3D faceModel;
        
        /// <summary>
        /// 에지 모델
        /// </summary>
        private GeometryModel3D edgeModel;
        
        /// <summary>
        /// 축 모델
        /// </summary>
        private GeometryModel3D axisModel;

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

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

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

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

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

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

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

        #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.camera = new PerspectiveCamera();

            this.camera.FieldOfView = 60;

            this.viewport3D.Camera = this.camera;

            SetCameraPosition();

            DefineLight();

            DefineModel(out this.faceModel, out this.edgeModel, out this.axisModel);

            this.modelGroup.Children.Add(this.faceModel);
            this.modelGroup.Children.Add(this.edgeModel);
            this.modelGroup.Children.Add(this.axisModel);

            ModelVisual3D modelVisual = new ModelVisual3D();

            modelVisual.Content = this.modelGroup;

            this.viewport3D.Children.Add(modelVisual);
        }

        #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;
                    }

                    break;

                case Key.Down :

                    this.cameraPhi -= CAMERA_DELTA_PHI;

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

                    break;

                case Key.Left :

                    this.cameraTheta += CAMEREA_DELTA_THETA;

                    break;

                case Key.Right :

                    this.cameraTheta -= CAMEREA_DELTA_THETA;

                    break;

                case Key.Add     :
                case Key.OemPlus :

                    this.cameraR -= CAMERA_DELTA_R;

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

                    break;

                case Key.Subtract :
                case Key.OemMinus :

                    this.cameraR += CAMERA_DELTA_R;

                    break;
            }

            SetCameraPosition();
        }

        #endregion
        #region 체크 박스 클릭시 처리하기 - checkBox_Click(sender, e)

        /// <summary>
        /// 체크 박스 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void checkBox_Click(object sender, RoutedEventArgs e)
        {
            this.modelGroup.Children.Clear();

            foreach(Light light in this.lightList)
            {
                this.modelGroup.Children.Add(light);
            }

            if(this.axisCheckBox.IsChecked.Value)
            {
                this.modelGroup.Children.Add(this.axisModel);
            }

            if(this.faceCheckBox.IsChecked.Value)
            {
                this.modelGroup.Children.Add(this.faceModel);
            }

            if(this.edgeCheckBox.IsChecked.Value)
            {
                this.modelGroup.Children.Add(this.edgeModel);
            }
        }

        #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()

        /// <summary>
        /// 조명 정의하기
        /// </summary>
        private void DefineLight()
        {
            Color color64  = Color.FromArgb(255, 128, 128, 64);
            Color color128 = Color.FromArgb(255, 255, 255, 128);

            this.lightList.Add(new AmbientLight(color64));

            this.lightList.Add(new DirectionalLight(color128, new Vector3D(-1.0, -2.0, -3.0)));
            this.lightList.Add(new DirectionalLight(color128, new Vector3D( 1.0,  2.0,  3.0)));

            foreach(Light light in this.lightList)
            {
                this.modelGroup.Children.Add(light);
            }
        }

        #endregion
        #region 스케일 벡터 구하기 - GetScaleVector(vector, length)

        /// <summary>
        /// 스케일 벡터 구하기
        /// </summary>
        /// <param name="vector">벡터</param>
        /// <param name="length">길이</param>
        /// <returns>스케일 벡터</returns>
        private Vector3D GetScaleVector(Vector3D vector, double length)
        {
            double scale = length / vector.Length;

            return new Vector3D
            (
                vector.X * scale,
                vector.Y * scale,
                vector.Z * scale
            );
        }

        #endregion
        #region 삼각형 추가하기 - AddTriangle(mesh, point1, point2, point3)

        /// <summary>
        /// 삼각형 추가하기
        /// </summary>
        /// <param name="mesh">메시</param>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        /// <param name="point3">포인트 3</param>
        private void AddTriangle(MeshGeometry3D mesh, Point3D point1, Point3D point2, Point3D point3)
        {
            int index = mesh.Positions.Count;

            mesh.Positions.Add(point1);
            mesh.Positions.Add(point2);
            mesh.Positions.Add(point3);

            mesh.TriangleIndices.Add(index++);
            mesh.TriangleIndices.Add(index++);
            mesh.TriangleIndices.Add(index  );
        }

        #endregion
        #region 세그먼트 추가하기 - AddSegment(mesh, point1, point2, upVector)

        /// <summary>
        /// 세그먼트 추가하기
        /// </summary>
        /// <param name="mesh">메시</param>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        /// <param name="upVector">UP 벡터</param>
        private void AddSegment(MeshGeometry3D mesh, Point3D point1, Point3D point2, Vector3D upVector)
        {
            const double THICKNESS = 0.01;

            Vector3D vector1 = point2 - point1;

            Vector3D scaleVector1 = GetScaleVector(upVector, THICKNESS / 2.0);

            Vector3D scaleVector2 = Vector3D.CrossProduct(vector1, scaleVector1);

            scaleVector2 = GetScaleVector(scaleVector2, THICKNESS / 2.0);

            Point3D p1pp = point1 + scaleVector1 + scaleVector2;
            Point3D p1mp = point1 - scaleVector1 + scaleVector2;
            Point3D p1pm = point1 + scaleVector1 - scaleVector2;
            Point3D p1mm = point1 - scaleVector1 - scaleVector2;
            Point3D p2pp = point2 + scaleVector1 + scaleVector2;
            Point3D p2mp = point2 - scaleVector1 + scaleVector2;
            Point3D p2pm = point2 + scaleVector1 - scaleVector2;
            Point3D p2mm = point2 - scaleVector1 - scaleVector2;

            AddTriangle(mesh, p1pp, p1mp, p2mp);
            AddTriangle(mesh, p1pp, p2mp, p2pp);

            AddTriangle(mesh, p1pp, p2pp, p2pm);
            AddTriangle(mesh, p1pp, p2pm, p1pm);

            AddTriangle(mesh, p1pm, p2pm, p2mm);
            AddTriangle(mesh, p1pm, p2mm, p1mm);

            AddTriangle(mesh, p1mm, p2mm, p2mp);
            AddTriangle(mesh, p1mm, p2mp, p1mp);

            AddTriangle(mesh, p1pp, p1pm, p1mm);
            AddTriangle(mesh, p1pp, p1mm, p1mp);

            AddTriangle(mesh, p2pp, p2mp, p2mm);
            AddTriangle(mesh, p2pp, p2mm, p2pm);
        }

        #endregion
        #region 인덱스 구하기 - GetIndex(character)

        /// <summary>
        /// 인덱스 구하기
        /// </summary>
        /// <param name="character">문자</param>
        /// <returns>인덱스</returns>
        private int GetIndex(char character)
        {
            return character - 'A';
        }

        #endregion
        #region 세그먼트 추가하기 - AddSegment(mesh, pointArray, name1, name2, upVector)

        /// <summary>
        /// 세그먼트 추가하기
        /// </summary>
        /// <param name="mesh">메시</param>
        /// <param name="pointArray">포인트 배열</param>
        /// <param name="name1">명칭 1</param>
        /// <param name="name2">명칭 2</param>
        /// <param name="upVector">UP 벡터</param>
        private void AddSegment(MeshGeometry3D mesh, Point3D[] pointArray, char name1, char name2, Vector3D upVector)
        {
            Point3D point1 = pointArray[GetIndex(name1)];
            Point3D point2 = pointArray[GetIndex(name2)];

            AddSegment(mesh, point1, point2, upVector);
        }

        #endregion
        #region 12면체 포인트 배열 구하기 - GetDodecahedronPointArray(sideLength)

        /// <summary>
        /// 12면체 포인트 배열 구하기
        /// </summary>
        /// <param name="sideLength">면 길이</param>
        /// <returns>12면체 포인트 배열</returns>
        private Point3D[] GetDodecahedronPointArray(double sideLength)
        {
            double s  = sideLength;
            double t2 = Math.PI / 10.0;
            double t3 = 3.0 * Math.PI / 10.0;
            double t4 = Math.PI / 5.0;
            double d1 = s / 2.0 / Math.Sin(t4);
            double d2 = d1 * Math.Cos(t4);
            double d3 = d1 * Math.Cos(t2);
            double d4 = d1 * Math.Sin(t2);
            double fx = (s * s - (2.0 * d3) * (2.0 * d3) - (d1 * d1 - d3 * d3 - d4 * d4)) / (2.0 * (d4 - d1));
            double d5 = Math.Sqrt(0.5 * (s * s + (2.0 * d3) * (2.0 * d3) - (d1 - fx) * (d1 - fx) - (d4 - fx) * (d4 - fx) - d3 * d3));
            double fy = (fx * fx - d1 * d1 - d5 * d5) / (2.0 * d5);
            double ay = d5 + fy;

            Point3D point1  = new Point3D( d1               ,  ay,  0                );
            Point3D point2  = new Point3D( d4               ,  ay,  d3               );
            Point3D point3  = new Point3D(-d2               ,  ay,  s / 2            );
            Point3D point4  = new Point3D(-d2               ,  ay, -s / 2            );
            Point3D point5  = new Point3D( d4               ,  ay, -d3               );
            Point3D point6  = new Point3D( fx               ,  fy,  0                );
            Point3D point7  = new Point3D( fx * Math.Sin(t2),  fy,  fx * Math.Cos(t2));
            Point3D point8  = new Point3D(-fx * Math.Sin(t3),  fy,  fx * Math.Cos(t3));
            Point3D point9  = new Point3D(-fx * Math.Sin(t3),  fy, -fx * Math.Cos(t3));
            Point3D point10 = new Point3D( fx * Math.Sin(t2),  fy, -fx * Math.Cos(t2));
            Point3D point11 = new Point3D( fx * Math.Sin(t3), -fy,  fx * Math.Cos(t3));
            Point3D point12 = new Point3D(-fx * Math.Sin(t2), -fy,  fx * Math.Cos(t2));
            Point3D point13 = new Point3D(-fx               , -fy,  0                );
            Point3D point14 = new Point3D(-fx * Math.Sin(t2), -fy, -fx * Math.Cos(t2));
            Point3D point15 = new Point3D( fx * Math.Sin(t3), -fy, -fx * Math.Cos(t3));
            Point3D point16 = new Point3D( d2               , -ay,  s / 2            );
            Point3D point17 = new Point3D(-d4               , -ay,  d3               );
            Point3D point18 = new Point3D(-d1               , -ay,  0                );
            Point3D point19 = new Point3D(-d4               , -ay, -d3               );
            Point3D point20 = new Point3D( d2               , -ay, -s / 2            );

            List<Point3D> pointList = new List<Point3D>();

            pointList.Add(point1 );
            pointList.Add(point2 );
            pointList.Add(point3 );
            pointList.Add(point4 );
            pointList.Add(point5 );
            pointList.Add(point6 );
            pointList.Add(point7 );
            pointList.Add(point8 );
            pointList.Add(point9 );
            pointList.Add(point10);
            pointList.Add(point11);
            pointList.Add(point12);
            pointList.Add(point13);
            pointList.Add(point14);
            pointList.Add(point15);
            pointList.Add(point16);
            pointList.Add(point17);
            pointList.Add(point18);
            pointList.Add(point19);
            pointList.Add(point20);

            return pointList.ToArray();
        }

        #endregion
        #region 다각형 추가하기 - AddPolygon(mesh, pointArray)

        /// <summary>
        /// 다각형 추가하기
        /// </summary>
        /// <param name="mesh">메시</param>
        /// <param name="pointArray">포인트 배열</param>
        private void AddPolygon(MeshGeometry3D mesh, params Point3D[] pointArray)
        {
            int index = mesh.Positions.Count;

            foreach(Point3D point in pointArray)
            {
                mesh.Positions.Add(point);
            }

            for(int i = 1; i < pointArray.Length - 1; i++)
            {
                mesh.TriangleIndices.Add(index        );
                mesh.TriangleIndices.Add(index + i    );
                mesh.TriangleIndices.Add(index + i + 1);
            }
        }

        #endregion
        #region 다각형 추가하기 - AddPolygon(mesh, pointNameList, pointArray)

        /// <summary>
        /// 다각형 추가하기
        /// </summary>
        /// <param name="mesh">메시</param>
        /// <param name="pointNameList">포인트명 리스트</param>
        /// <param name="pointArray">포인트 배열</param>
        private void AddPolygon(MeshGeometry3D mesh, string pointNameList, Point3D[] pointArray)
        {
            Point3D[] polygonPointArray = new Point3D[pointNameList.Length];

            for(int i = 0; i < pointNameList.Length; i++)
            {
                polygonPointArray[i] = pointArray[GetIndex(pointNameList[i])];
            }

            AddPolygon(mesh, polygonPointArray);
        }

        #endregion
        #region 다각형 세그먼트 추가하기 - AddPolygonSegment(mesh, pointNameList, pointArray, upVector)

        /// <summary>
        /// 다각형 세그먼트 추가하기
        /// </summary>
        /// <param name="mesh">메시</param>
        /// <param name="pointNameList">포인트명 리스트</param>
        /// <param name="pointArray">포인트 배열</param>
        /// <param name="upVector">UP 벡터</param>
        private void AddPolygonSegment(MeshGeometry3D mesh, string pointNameList, Point3D[] pointArray, Vector3D upVector)
        {
            for(int i = 0; i < pointNameList.Length; i++)
            {
                char character1 = pointNameList[i];
                char character2 = pointNameList[(i + 1) % pointNameList.Length];

                AddSegment(mesh, pointArray, character1, character2, upVector);
            }
        }

        #endregion
        #region 모델 정의하기 - DefineModel(faceModel, edgeModel, axisModel)

        /// <summary>
        /// 모델 정의하기
        /// </summary>
        /// <param name="faceModel">면 모델</param>
        /// <param name="edgeModel">에지 모델</param>
        /// <param name="axisModel">축 모델</param>
        private void DefineModel(out GeometryModel3D faceModel, out GeometryModel3D edgeModel, out GeometryModel3D axisModel)
        {
            // 축 모델
            MeshGeometry3D axisMesh = new MeshGeometry3D();

            Point3D originPoint   = new Point3D(0, 0, 0);
            Point3D maximumXPoint = new Point3D(1.5, 0, 0);
            Point3D maximumYPoint = new Point3D(0, 1.5, 0);
            Point3D maximumZPoint = new Point3D(0, 0, 1.5);

            AddSegment(axisMesh, originPoint, maximumXPoint, new Vector3D(0, 1, 0));
            AddSegment(axisMesh, originPoint, maximumZPoint, new Vector3D(0, 1, 0));
            AddSegment(axisMesh, originPoint, maximumYPoint, new Vector3D(1, 0, 0));

            SolidColorBrush axixBrush = Brushes.Red;

            DiffuseMaterial axixMaterial = new DiffuseMaterial(axixBrush);

            axisModel = new GeometryModel3D(axisMesh, axixMaterial);


            Point3D[] pointArray = GetDodecahedronPointArray(1);


            // 면 모델
            MeshGeometry3D faceMesh = new MeshGeometry3D();

            AddPolygon(faceMesh, "EDCBA", pointArray);
            AddPolygon(faceMesh, "ABGKF", pointArray);
            AddPolygon(faceMesh, "AFOJE", pointArray);
            AddPolygon(faceMesh, "EJNID", pointArray);
            AddPolygon(faceMesh, "DIMHC", pointArray);
            AddPolygon(faceMesh, "CHLGB", pointArray);
            AddPolygon(faceMesh, "KPTOF", pointArray);
            AddPolygon(faceMesh, "OTSNJ", pointArray);
            AddPolygon(faceMesh, "NSRMI", pointArray);
            AddPolygon(faceMesh, "MRQLH", pointArray);
            AddPolygon(faceMesh, "LQPKG", pointArray);
            AddPolygon(faceMesh, "PQRST", pointArray);

            SolidColorBrush faceBrush = Brushes.Orange;

            DiffuseMaterial faceMaterial = new DiffuseMaterial(faceBrush);

            faceModel = new GeometryModel3D(faceMesh, faceMaterial);


            // 에지 모델
            MeshGeometry3D edgeMesh = new MeshGeometry3D();

            Vector3D upVector = new Vector3D(0, 1, 0);

            AddPolygonSegment(edgeMesh, "EDCBA", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "ABGKF", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "AFOJE", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "EJNID", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "DIMHC", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "CHLGB", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "KPTOF", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "OTSNJ", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "NSRMI", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "MRQLH", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "LQPKG", pointArray, upVector);
            AddPolygonSegment(edgeMesh, "PQRST", pointArray, upVector);

            SolidColorBrush edgeBrush = Brushes.Brown;

            DiffuseMaterial edgeMaterial = new DiffuseMaterial(edgeBrush);

            edgeModel = new GeometryModel3D(edgeMesh, edgeMaterial);
        }

        #endregion
    }
}
728x90
반응형
Posted by 사용자 icodebroker
TAG , , ,

댓글을 달아 주세요