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

728x90
반응형

TestProject.zip
다운로드

▶ Edge.cs

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

namespace TestProject
{
    /// <summary>
    /// 에지
    /// </summary>
    public class Edge : IEquatable<Edge>
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 포인트 1
        /// </summary>
        public Point3D Point1;
        
        /// <summary>
        /// 포인트 2
        /// </summary>
        public Point3D Point2;

        #endregion

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

        #region 생성자 - Edge(point1, point2)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        public Edge(Point3D point1, Point3D point2)
        {
            Point1 = point1;
            Point2 = point2;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 동일 여부 구하기 - Equals(other)

        /// <summary>
        /// 동일 여부 구하기
        /// </summary>
        /// <param name="other">다른 에지</param>
        /// <returns>동일 여부</returns>
        public bool Equals(Edge other)
        {
            Vector3D vector1 = this.Point1 - other.Point1;
            Vector3D vector2 = this.Point2 - other.Point2;

            if((vector1.Length < 0.001) && (vector2.Length < 0.001))
            {
                return true;
            }

            vector1 = this.Point2 - other.Point1;
            vector2 = this.Point1 - other.Point2;

            if((vector1.Length < 0.001) && (vector2.Length < 0.001))
            {
                return true;
            }

            return false;
        }

        #endregion
    }
}

 

▶ Triangle.cs

using System;
using System.Collections.Generic;
using System.Windows.Media.Media3D;

namespace TestProject
{
    /// <summary>
    /// 삼각형
    /// </summary>
    public class Triangle
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 포인트 배열
        /// </summary>
        public Point3D[] PointArray;

        #endregion

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

        #region 생성자 - Triangle(pointArray)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="pointArray">포인트 배열</param>
        public Triangle(params Point3D[] pointArray)
        {
            PointArray = pointArray;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 세분화 하기 - Subdivide(triangleList, centerPoint, radius)

        /// <summary>
        /// 세분화 하기
        /// </summary>
        /// <param name="triangleList">삼각형 리스트</param>
        /// <param name="centerPoint">중심 포인트</param>
        /// <param name="radius">반지름</param>
        public void Subdivide(List<Triangle> triangleList, Point3D centerPoint, double radius)
        {
            Vector3D v10 = PointArray[1] - PointArray[0];
            Vector3D v20 = PointArray[2] - PointArray[0];
            Vector3D v21 = PointArray[2] - PointArray[1];

            Point3D point1 = PointArray[0] + v10 * 1.0 / 3.0;
            Point3D point2 = PointArray[0] + v20 * 1.0 / 3.0;
            Point3D point3 = PointArray[0] + v10 * 2.0 / 3.0;
            Point3D point4 = PointArray[0] + v10 * 2.0 / 3.0 + v21 * 1.0 / 3.0;
            Point3D point5 = PointArray[0] + v20 * 2.0 / 3.0;
            Point3D point6 = PointArray[1] + v21 * 1.0 / 3.0;
            Point3D point7 = PointArray[1] + v21 * 2.0 / 3.0;

            NormalizePoint(ref point1, centerPoint, radius);
            NormalizePoint(ref point2, centerPoint, radius);
            NormalizePoint(ref point3, centerPoint, radius);
            NormalizePoint(ref point4, centerPoint, radius);
            NormalizePoint(ref point5, centerPoint, radius);
            NormalizePoint(ref point6, centerPoint, radius);
            NormalizePoint(ref point7, centerPoint, radius);

            triangleList.Add(new Triangle(PointArray[0], point1       , point2       ));
            triangleList.Add(new Triangle(point1       , point3       , point4       ));
            triangleList.Add(new Triangle(point1       , point4       , point2       ));
            triangleList.Add(new Triangle(point2       , point4       , point5       ));
            triangleList.Add(new Triangle(point3       , PointArray[1], point6       ));
            triangleList.Add(new Triangle(point3       , point6       , point4       ));
            triangleList.Add(new Triangle(point4       , point6       , point7       ));
            triangleList.Add(new Triangle(point4       , point7       , point5       ));
            triangleList.Add(new Triangle(point5       , point7       , PointArray[2]));
        }

        #endregion
        #region 각도 배열 구하기 - GetAngleArray()

        /// <summary>
        /// 각도 배열 구하기
        /// </summary>
        /// <returns>각도 배열</returns>
        public double[] GetAngleArray()
        {
            Vector3D v10 = PointArray[1] - PointArray[0];
            Vector3D v21 = PointArray[2] - PointArray[1];
            Vector3D v02 = PointArray[0] - PointArray[2];

            v10.Normalize();
            v21.Normalize();
            v02.Normalize();

            double angle0 = Math.Acos(Vector3D.DotProduct(v10, -v02)) * 180.0 / Math.PI;
            double angle1 = Math.Acos(Vector3D.DotProduct(v21, -v10)) * 180.0 / Math.PI;
            double angle2 = Math.Acos(Vector3D.DotProduct(v02, -v21)) * 180.0 / Math.PI;

            double[] angleArray = new double[] { angle0, angle1, angle2 };

            Array.Sort(angleArray);

            return angleArray;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 포인트 정규화 하기 - NormalizePoint(point, centerPoint, distance)

        /// <summary>
        /// 포인트 정규화 하기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <param name="centerPoint">중심 포인트</param>
        /// <param name="distance">거리</param>
        private void NormalizePoint(ref Point3D point, Point3D centerPoint, double distance)
        {
            Vector3D vector = point - centerPoint;

            point = centerPoint + vector / vector.Length * distance;
        }

        #endregion
    }
}

 

▶ 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="측지 구(Geodesic Sphere) 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16"
    Loaded="Window_Loaded">
    <Grid>
        <Grid.Resources>
            <Style TargetType="CheckBox">
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5 0 10 0" />
            </Style>
            <Style TargetType="Label">
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5 0 0 0" />
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="20" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="20" />
        </Grid.ColumnDefinitions>
        <Viewport3D Name="viewport3D" Grid.Row="1" Grid.Column="0" />
        <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal">
            <CheckBox Name="axisCheckBox"
                IsChecked="True"
                Content="축"
                Click="checkBox_Click" />
            <CheckBox Name="edgeCheckBox"
                IsChecked="True"
                Content="에지"
                Click="checkBox_Click" />
            <CheckBox Name="faceCheckBox"
                Content="면"
                IsChecked="True"
                Click="checkBox_Click" />
            <StackPanel Orientation="Horizontal">
                <Label Content="레벨" />
                <Label Name="divisionLevelLabel"
                    Margin="0"
                    Width="20"
                    HorizontalContentAlignment="Right"
                    Content="1" />
                <StackPanel Orientation="Vertical">
                    <Button Name="btnUp"
                        IsTabStop="False"
                        Click="divisionLevelUpButton_Click">
                        <Image Source="Up.png" />
                    </Button>
                    <Button Name="btnDown"
                        IsTabStop="False"
                        Click="divisionLevelDownButton_Click">
                        <Image Source="Down.png" />
                    </Button>
                </StackPanel>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="거리"/>
                <Label Name="distanceLabel"
                    Content="3.0"
                    Margin="0"
                    Width="35"
                    HorizontalContentAlignment="Right" />
                <StackPanel Orientation="Vertical">
                    <Button Name="btnDistUp"
                        IsTabStop="False"
                        Click="distanceUpButton_Click">
                        <Image Source="Up.png" />
                    </Button>
                    <Button Name="btnDistDown"
                        IsTabStop="False"
                        Click="distanceDownButton_Click">
                        <Image Source="Down.png" />
                    </Button>
                </StackPanel>
            </StackPanel>
        </StackPanel>
        <ScrollBar Name="cameraThetaScrollBar" Grid.Row="2" Grid.Column="0"
            Orientation="Horizontal"
            Minimum="0"
            Maximum="360"
            Value="21.8"
            Scroll="cameraThetaScrollBar_Scroll" />
        <ScrollBar Name="cameraPhiScrollBar" Grid.Column="5" Grid.Row="1"
            Orientation="Vertical"
            Minimum="-90"
            Maximum="90"
            Value="44.5"
            Scroll="cameraPhiScrollBar_Scroll" />
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls.Primitives;
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;
        
        /// <summary>
        /// 카메라 세타
        /// </summary>
        private double cameraTheta;
        
        /// <summary>
        /// 카메라 R
        /// </summary>
        private double cameraR;

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

        /// <summary>
        /// 분할 레벨
        /// </summary>
        private int divisionLevel = 0;

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

            this.cameraTheta   = this.cameraThetaScrollBar.Value * Math.PI / 180.0;
            this.cameraPhi     = this.cameraPhiScrollBar.Value * Math.PI / 180.0;
            this.cameraR       = double.Parse(this.distanceLabel.Content.ToString());
            this.divisionLevel = int.Parse(this.divisionLevelLabel.Content.ToString());

            SetCameraPosition();

            DefineLight();

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

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

            ModelVisual3D modelVisual = new ModelVisual3D();

            modelVisual.Content = this.modelGroup;

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

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

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

        #endregion
        #region 분할 레벨 UP 버튼 클릭시 처리하기 - divisionLevelUpButton_Click(sender, e)

        /// <summary>
        /// 분할 레벨 UP 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void divisionLevelUpButton_Click(object sender, RoutedEventArgs e)
        {
            if(this.divisionLevel == 3)
            {
                return;
            }

            this.divisionLevel++;

            this.divisionLevelLabel.Content = this.divisionLevel;

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

            DisplaySelectedModel();
        }

        #endregion
        #region 분할 레벨 DOWN 버튼 클릭시 처리하기 - divisionLevelDownButton_Click(sender, e)

        /// <summary>
        /// 분할 레벨 DOWN 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void divisionLevelDownButton_Click(object sender, RoutedEventArgs e)
        {
            if(this.divisionLevel == 0)
            {
                return;
            }

            this.divisionLevel--;

            this.divisionLevelLabel.Content = this.divisionLevel;

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

            DisplaySelectedModel();
        }

        #endregion
        #region 거리 UP 버튼 클릭시 처리하기 - distanceUpButton_Click(sender, e)

        /// <summary>
        /// 거리 UP 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void distanceUpButton_Click(object sender, RoutedEventArgs e)
        {
            this.cameraR += CAMERA_DELTA_R;

            this.distanceLabel.Content = this.cameraR.ToString("0.0");

            SetCameraPosition();
        }

        #endregion
        #region 거리 DOWN 버튼 클릭시 처리하기 - distanceDownButton_Click(sender, e)

        /// <summary>
        /// 거리 DOWN 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void distanceDownButton_Click(object sender, RoutedEventArgs e)
        {
            this.cameraR -= CAMERA_DELTA_R;

            this.distanceLabel.Content = this.cameraR.ToString("0.0");

            SetCameraPosition();
        }

        #endregion
        #region 카메라 파이 스크롤바 스크롤시 처리하기 - cameraPhiScrollBar_Scroll(sender, e)

        /// <summary>
        /// 카메라 파이 스크롤바 스크롤시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void cameraPhiScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            this.cameraPhi = this.cameraPhiScrollBar.Value * Math.PI / 180.0;

            SetCameraPosition();
        }

        #endregion
        #region 카메라 세타 스크롤바 스크롤시 처리하기 - cameraThetaScrollBar_Scroll(sender, e)

        /// <summary>
        /// 카메라 세타 스크롤바 스크롤시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void cameraThetaScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            this.cameraTheta = this.cameraThetaScrollBar.Value * Math.PI / 180.0;

            SetCameraPosition();
        }

        #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(color64, new Vector3D(-1.0, -2.0, -3.0)));
            this.lightList.Add(new DirectionalLight(color64, 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 삼각형 추가하기 - AddTriangle(mesh, triangle)

        /// <summary>
        /// 삼각형 추가하기
        /// </summary>
        /// <param name="mesh">메시</param>
        /// <param name="triangle">삼각형</param>
        private void AddTriangle(MeshGeometry3D mesh, Triangle triangle)
        {
            int index = mesh.Positions.Count;

            mesh.Positions.Add(triangle.PointArray[0]);
            mesh.Positions.Add(triangle.PointArray[1]);
            mesh.Positions.Add(triangle.PointArray[2]);

            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 포인트 배열 구하기 - GetPointArray(sideLength)

        /// <summary>
        /// 포인트 배열 구하기
        /// </summary>
        /// <param name="sideLength">면 길이</param>
        /// <returns>포인트 배열</returns>
        private Point3D[] GetPointArray(double sideLength)
        {
            double s  = sideLength;
            double t2 = Math.PI / 10.0;
            double t4 = Math.PI / 5.0;
            double r  = (s / 2.0) / Math.Sin(t4);
            double h  = Math.Cos(t4) * r;
            double cx = r * Math.Sin(t2);
            double cz = r * Math.Cos(t2);
            double h1 = Math.Sqrt(s * s - r * r);
            double h2 = Math.Sqrt((h + r) * (h + r) - h * h);
            double y2 = (h2 - h1) / 2.0;
            double y1 = y2 + h1;

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

            pointList.Add(new Point3D( 0 ,  y1, 0     ));
            pointList.Add(new Point3D( r ,  y2, 0     ));
            pointList.Add(new Point3D( cx,  y2, cz    ));
            pointList.Add(new Point3D(-h ,  y2, s / 2 ));
            pointList.Add(new Point3D(-h ,  y2, -s / 2));
            pointList.Add(new Point3D( cx,  y2, -cz   ));
            pointList.Add(new Point3D(-r , -y2, 0     ));
            pointList.Add(new Point3D(-cx, -y2, -cz   ));
            pointList.Add(new Point3D( h , -y2, -s / 2));
            pointList.Add(new Point3D( h , -y2, s / 2 ));
            pointList.Add(new Point3D(-cx, -y2, cz    ));
            pointList.Add(new Point3D( 0 , -y1, 0     ));

            return pointList.ToArray();
        }

        #endregion
        #region 삼각형 배열 구하기 - GetTriangleArray(sideLength)

        /// <summary>
        /// 삼각형 배열 구하기
        /// </summary>
        /// <param name="sideLength">면 길이</param>
        /// <returns>삼각형 배열</returns>
        private Triangle[] GetTriangleArray(double sideLength)
        {
            Point3D[] pointArray = GetPointArray(sideLength);

            List<Triangle> triangleList = new List<Triangle>();

            triangleList.Add(new Triangle(pointArray[0], pointArray[2], pointArray[1]));
            triangleList.Add(new Triangle(pointArray[0], pointArray[3], pointArray[2]));
            triangleList.Add(new Triangle(pointArray[0], pointArray[4], pointArray[3]));
            triangleList.Add(new Triangle(pointArray[0], pointArray[5], pointArray[4]));
            triangleList.Add(new Triangle(pointArray[0], pointArray[1], pointArray[5]));

            triangleList.Add(new Triangle(pointArray[1], pointArray[2], pointArray[9 ]));
            triangleList.Add(new Triangle(pointArray[2], pointArray[3], pointArray[10]));
            triangleList.Add(new Triangle(pointArray[3], pointArray[4], pointArray[6 ]));
            triangleList.Add(new Triangle(pointArray[4], pointArray[5], pointArray[7 ]));
            triangleList.Add(new Triangle(pointArray[5], pointArray[1], pointArray[8 ]));

            triangleList.Add(new Triangle(pointArray[6 ], pointArray[4], pointArray[7 ]));
            triangleList.Add(new Triangle(pointArray[7 ], pointArray[5], pointArray[8 ]));
            triangleList.Add(new Triangle(pointArray[8 ], pointArray[1], pointArray[9 ]));
            triangleList.Add(new Triangle(pointArray[9 ], pointArray[2], pointArray[10]));
            triangleList.Add(new Triangle(pointArray[10], pointArray[3], pointArray[6 ]));

            triangleList.Add(new Triangle(pointArray[11], pointArray[6 ], pointArray[7 ]));
            triangleList.Add(new Triangle(pointArray[11], pointArray[7 ], pointArray[8 ]));
            triangleList.Add(new Triangle(pointArray[11], pointArray[8 ], pointArray[9 ]));
            triangleList.Add(new Triangle(pointArray[11], pointArray[9 ], pointArray[10]));
            triangleList.Add(new Triangle(pointArray[11], pointArray[10], pointArray[6 ]));

            double radius = pointArray[0].Y;

            Point3D originPoint = new Point3D(0, 0, 0);

            for(int i = 0; i < this.divisionLevel; i++)
            {
                List<Triangle> newTriangleList = new List<Triangle>();

                foreach(Triangle triangle in triangleList)
                {
                    triangle.Subdivide(newTriangleList, originPoint, radius);
                }

                triangleList = newTriangleList;
            }

            return triangleList.ToArray();
        }

        #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)
        {
            Cursor = Cursors.Wait;

            // 축 모델
            MeshGeometry3D axisMesh = new MeshGeometry3D();

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

            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 axisBrush = Brushes.Red;

            DiffuseMaterial axisMaterial = new DiffuseMaterial(axisBrush);

            axisModel = new GeometryModel3D(axisMesh, axisMaterial);


            Triangle[] triangleArray = GetTriangleArray(1);


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

            foreach(Triangle triangle in triangleArray)
            {
                AddTriangle(faceMesh, triangle);
            }

            SolidColorBrush faceBrush = Brushes.LightYellow;

            DiffuseMaterial faceMaterial = new DiffuseMaterial(faceBrush);

            faceModel = new GeometryModel3D(faceMesh, faceMaterial);

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

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

            HashSet<Edge> edgeHashSet = new HashSet<Edge>();

            foreach(Triangle triangle in triangleArray)
            {
                for(int i = 0; i < 2; i++)
                {
                    Edge edge = new Edge
                    (
                        triangle.PointArray[i          ],
                        triangle.PointArray[(i + 1) % 3]
                    );

                    if(!edgeHashSet.Contains(edge))
                    {
                        edgeHashSet.Add(edge);
                    }
                }
            }

            foreach(Edge edge in edgeHashSet)
            {
                AddSegment(edgeMesh, edge.Point1, edge.Point2, upVector);
            }

            SolidColorBrush edgeBrush = Brushes.Goldenrod;

            DiffuseMaterial edgeMaterial = new DiffuseMaterial(edgeBrush);

            edgeModel = new GeometryModel3D(edgeMesh, edgeMaterial);

            List<string> uniqueAngleList = new List<string>();

            foreach(Triangle triangle in triangleArray)
            {
                double[] angleArray = triangle.GetAngleArray();

                string combined = angleArray[0].ToString("0.0000") + ", " +
                                  angleArray[1].ToString("0.0000") + ", " +
                                  angleArray[2].ToString("0.0000");

                if(!uniqueAngleList.Contains(combined))
                {
                    uniqueAngleList.Add(combined);
                }
            }

            string[] sortedUniqueArray = uniqueAngleList.ToArray();

            Array.Sort(sortedUniqueArray);

            Cursor = null;
        }

        #endregion
        #region 선택 모델 표시하기 - DisplaySelectedModel()

        /// <summary>
        /// 선택 모델 표시하기
        /// </summary>
        private void DisplaySelectedModel()
        {
            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
    }
}
728x90
반응형
Posted by 사용자 icodebroker
TAG , , ,

댓글을 달아 주세요