728x90
반응형
728x170
▶ PanoramaViewer.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace TestProject
{
/// <summary>
/// 파노라마 뷰어
/// </summary>
public class PanoramaViewer : Viewport3D
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 시야각 속성 - FieldOfViewProperty
/// <summary>
/// 시야각 속성
/// </summary>
public static readonly DependencyProperty FieldOfViewProperty = DependencyProperty.Register
(
"FieldOfView",
typeof(double),
typeof(PanoramaViewer),
new PropertyMetadata
(
(double)0,
new PropertyChangedCallback(FieldOfViewPropertyChangedCallback)
)
);
#endregion
#region X축 회전 속성 - RotationXProperty
/// <summary>
/// X축 회전 속성
/// </summary>
public static readonly DependencyProperty RotationXProperty = DependencyProperty.Register
(
"RotationX",
typeof(double),
typeof(PanoramaViewer),
new UIPropertyMetadata
(
0.0,
(d, e) => ((PanoramaViewer)d).UpdateRotation()
)
);
#endregion
#region Y축 회전 속성 - RotationYProperty
/// <summary>
/// Y축 회전 속성
/// </summary>
public static readonly DependencyProperty RotationYProperty = DependencyProperty.Register
(
"RotationY",
typeof(double),
typeof(PanoramaViewer),
new UIPropertyMetadata
(
0.0,
(d, e) => ((PanoramaViewer)d).UpdateRotation()
)
);
#endregion
#region Z축 회전 속성 - RotationZProperty
/// <summary>
/// Z축 회전 속성
/// </summary>
public static readonly DependencyProperty RotationZProperty = DependencyProperty.Register
(
"RotationZ",
typeof(double),
typeof(PanoramaViewer),
new UIPropertyMetadata
(
0.0,
(d, e) => ((PanoramaViewer)d).UpdateRotation()
)
);
#endregion
#region 파노라마 이미지 속성 - PanoramaImageProperty
/// <summary>
/// 파노라마 이미지 속성
/// </summary>
public static readonly DependencyProperty PanoramaImageProperty = DependencyProperty.Register
(
"PanoramaImage",
typeof(ImageSource),
typeof(PanoramaViewer),
new PropertyMetadata
(
null,
new PropertyChangedCallback(PanoramaImagePropertyChangedCallback)
)
);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// X축 벡터
/// </summary>
private static readonly Vector3D _xAxisVector = new Vector3D(1, 0, 0);
/// <summary>
/// Y축 벡터
/// </summary>
private static readonly Vector3D _yAxisVector = new Vector3D(0, 1, 0);
/// <summary>
/// Z축 벡터
/// </summary>
private static readonly Vector3D _zAxisVector = new Vector3D(0, 0, 1);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 시야각 - FieldOfView
/// <summary>
/// 시야각
/// </summary>
public double FieldOfView
{
get
{
return (double)GetValue(FieldOfViewProperty);
}
set
{
SetValue(FieldOfViewProperty, value);
}
}
#endregion
#region X축 회전 - RotationX
/// <summary>
/// X축 회전
/// </summary>
public double RotationX
{
get
{
return (double)GetValue(RotationXProperty);
}
set
{
SetValue(RotationXProperty, value);
}
}
#endregion
#region Y축 회전 - RotationY
/// <summary>
/// Y축 회전
/// </summary>
public double RotationY
{
get
{
return (double)GetValue(RotationYProperty);
}
set
{
SetValue(RotationYProperty, value);
}
}
#endregion
#region Z축 회전 - RotationZ
/// <summary>
/// Z축 회전
/// </summary>
public double RotationZ
{
get
{
return (double)GetValue(RotationZProperty);
}
set
{
SetValue(RotationZProperty, value);
}
}
#endregion
#region 파노라마 이미지 - PanoramaImage
/// <summary>
/// 파노라마 이미지
/// </summary>
public ImageSource PanoramaImage
{
get
{
return (ImageSource)GetValue(PanoramaImageProperty);
}
set
{
SetValue(PanoramaImageProperty, value);
}
}
#endregion
#region 파노라마 객체 - PanoramaObject
/// <summary>
/// 파노라마 객체
/// </summary>
public ModelVisual3D PanoramaObject { get; set; }
#endregion
#region 파노라마 지오메트리 - PanoramaGeometry
/// <summary>
/// 파노라마 지오메트리
/// </summary>
public GeometryModel3D PanoramaGeometry { get; set; }
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 파노라마 회전 - PanoramaRotation
/// <summary>
/// 파노라마 회전
/// </summary>
private QuaternionRotation3D PanoramaRotation { get; set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - PanoramaViewer()
/// <summary>
/// 생성자
/// </summary>
public PanoramaViewer()
{
InitializeViewer();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 시야각 속성 변경시 콜백 처리하기 - FieldOfViewPropertyChangedCallback(sender, e)
/// <summary>
/// 시야각 속성 변경시 콜백 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private static void FieldOfViewPropertyChangedCallback(object sender, DependencyPropertyChangedEventArgs e)
{
PanoramaViewer viewer = sender as PanoramaViewer;
PerspectiveCamera camera = viewer.Camera as PerspectiveCamera;
camera.FieldOfView = viewer.FieldOfView;
}
#endregion
#region 파노라마 이미지 속성 변경시 콜백 처리하기 - PanoramaImagePropertyChangedCallback(sender, e)
/// <summary>
/// 파노라마 이미지 속성 변경시 콜백 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private static void PanoramaImagePropertyChangedCallback(object sender, DependencyPropertyChangedEventArgs e)
{
PanoramaViewer viewer = sender as PanoramaViewer;
ImageBrush brush = new ImageBrush(viewer.PanoramaImage);
viewer.PanoramaGeometry.BackMaterial = new DiffuseMaterial(brush);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region 위치 구하기 - GetPosition(theta, y)
/// <summary>
/// 위치 구하기
/// </summary>
/// <param name="theta">세타</param>
/// <param name="y">Y</param>
/// <returns>위치</returns>
private Point3D GetPosition(double theta, double y)
{
double r = Math.Sqrt(1 - y * y);
double x = r * Math.Cos(theta);
double z = r * Math.Sin(theta);
return new Point3D(x, y, z);
}
#endregion
#region 법선 벡터 구하기 - GetNormalVector(t, y)
/// <summary>
/// 법선 벡터 구하기
/// </summary>
/// <param name="t">T</param>
/// <param name="y">Y</param>
/// <returns>법선</returns>
private Vector3D GetNormalVector(double t, double y)
{
return (Vector3D)GetPosition(t, y);
}
#endregion
#region 텍스처 좌표 구하기 - GetTextureCoordinate(t, y)
/// <summary>
/// 텍스처 좌표 구하기
/// </summary>
/// <param name="t">T</param>
/// <param name="y">Y</param>
/// <returns>텍스처 좌표</returns>
private Point GetTextureCoordinate(double t, double y)
{
Matrix matrix = new Matrix();
matrix.Scale(1 / (2 * Math.PI), -0.5);
Point point = new Point(t, y);
point = point * matrix;
return point;
}
#endregion
#region 지오메트리 생성하기 - CreateGeometry()
/// <summary>
/// 지오메트리 생성하기
/// </summary>
/// <returns>지오메트리</returns>
private Geometry3D CreateGeometry()
{
int thetaDivision = 64;
int yDivision = 64;
double maximumTheta = (360.0 / 180.0) * Math.PI;
double minimumY = -1.0;
double maximumY = 1.0;
double deltaTheta = maximumTheta / thetaDivision;
double deltaY = (maximumY - minimumY) / yDivision;
MeshGeometry3D meshGeometry = new MeshGeometry3D();
for(int i = 0; i <= yDivision; i++)
{
double y = minimumY + i * deltaY;
for(int j = 0; j <= thetaDivision; j++)
{
double t = j * deltaTheta;
meshGeometry.Positions.Add(GetPosition(t, y));
meshGeometry.Normals.Add(GetNormalVector(t, y));
meshGeometry.TextureCoordinates.Add(GetTextureCoordinate(t, y));
}
}
for(int i = 0; i < yDivision; i++)
{
for(int j = 0; j < thetaDivision; j++)
{
int x0 = j;
int x1 = (j + 1);
int y0 = i * (thetaDivision + 1);
int y1 = (i + 1) * (thetaDivision + 1);
meshGeometry.TriangleIndices.Add(x0 + y0);
meshGeometry.TriangleIndices.Add(x0 + y1);
meshGeometry.TriangleIndices.Add(x1 + y0);
meshGeometry.TriangleIndices.Add(x1 + y0);
meshGeometry.TriangleIndices.Add(x0 + y1);
meshGeometry.TriangleIndices.Add(x1 + y1);
}
}
meshGeometry.Freeze();
return meshGeometry;
}
#endregion
#region 뷰어 초기화 하기 - InitializeViewer()
/// <summary>
/// 뷰어 초기화 하기
/// </summary>
private void InitializeViewer()
{
PerspectiveCamera perspectiveCamera = new PerspectiveCamera();
perspectiveCamera.Position = new Point3D(0, -0.0, 0);
perspectiveCamera.UpDirection = new Vector3D(0, 1, 0);
perspectiveCamera.LookDirection = new Vector3D(0, 0, 1);
perspectiveCamera.FieldOfView = 80;
Camera = perspectiveCamera;
FieldOfView = 80;
ModelVisual3D lightModelVisual3D = new ModelVisual3D();
lightModelVisual3D.Content = new DirectionalLight(Colors.White, new Vector3D(0, 0, 1));
Children.Add(lightModelVisual3D);
PanoramaGeometry = new GeometryModel3D();
PanoramaGeometry.Geometry = CreateGeometry();
PanoramaObject = new ModelVisual3D();
PanoramaObject.Content = PanoramaGeometry;
RotateTransform3D rotateTransform = new RotateTransform3D();
ScaleTransform3D scaleTransform = new ScaleTransform3D() { ScaleX = 1, ScaleY = 1.65, ScaleZ = 1 };
Transform3DGroup transformGroup = new Transform3DGroup();
PanoramaRotation = new QuaternionRotation3D();
transformGroup.Children.Add(scaleTransform );
transformGroup.Children.Add(rotateTransform);
rotateTransform.Rotation = PanoramaRotation;
PanoramaObject.Transform = transformGroup;
Children.Add(PanoramaObject);
}
#endregion
#region 회전 갱신하기 - UpdateRotation()
/// <summary>
/// 회전 갱신하기
/// </summary>
private void UpdateRotation()
{
Quaternion quaternionX = new Quaternion(_xAxisVector, RotationX);
Quaternion quaternionY = new Quaternion(_yAxisVector, RotationY);
Quaternion quaternionZ = new Quaternion(_zAxisVector, RotationZ);
PanoramaRotation.Quaternion = quaternionX * quaternionY * quaternionZ;
}
#endregion
}
}
728x90
▶ 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"
xmlns:local="clr-namespace:TestProject"
Width="800"
Height="600"
Title="파노라마 뷰 사용하기"
FontFamily="나눔고딕코딩"
FontSize="16">
<Grid>
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Border
Margin="10"
BorderThickness="1"
BorderBrush="Gray">
<Grid>
<local:PanoramaViewer x:Name="viewer"
Height="350"
PanoramaImage="sample1.jpg" />
</Grid>
</Border>
<StackPanel Margin="10 0 10 0">
<StackPanel Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
Text="시야각"/>
<Slider
VerticalAlignment="Center"
Margin="10 0 0 0"
Width="50"
Minimum="30"
Maximum="150"
Value="{Binding FieldOfView, ElementName=viewer}" />
<TextBlock
Margin="10 0 0 0"
VerticalAlignment="Center"
Text="X축 회전" />
<Slider
VerticalAlignment="Center"
Margin="10 0 0 0"
Width="90"
Minimum="-180"
Maximum="180"
Value="{Binding RotationX, ElementName=viewer}" />
<TextBlock
VerticalAlignment="Center"
Margin="10 0 0 0"
Text="Y축 회전" />
<Slider
VerticalAlignment="Center"
Margin="10 0 0 0"
Width="90"
Minimum="-180"
Maximum="180"
Value="{Binding RotationY, ElementName=viewer}" />
<TextBlock
VerticalAlignment="Center"
Margin="10 0 0 0"
Text="Z축 회전" />
<Slider
VerticalAlignment="Center"
Margin="10 0 0 0"
Width="90"
Minimum="-180"
Maximum="180"
Value="{Binding RotationZ, ElementName=viewer}" />
</StackPanel>
</StackPanel>
<StackPanel
Margin="10 10 10 0"
HorizontalAlignment="Center"
Orientation="Horizontal">
<Button Name="button1"
Margin="10"
Padding="5"
Content="샘플 이미지 1" />
<Button Name="button2"
Margin="10"
Padding="5"
Content="샘플 이미지 2" />
<Button Name="button3"
Margin="10"
Padding="5"
Content="샘플 이미지 3" />
<Button Name="button4"
Margin="10"
Padding="5"
Content="샘플 이미지 4" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
300x250
▶ MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 회전 벡터
/// </summary>
private Vector rotationVector = new Vector();
/// <summary>
/// 드래그 시작 포인트
/// </summary>
private Point dragStartPoint;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow()
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
this.viewer.MouseLeftButtonDown += viewer_MouseLeftButtonDown;
this.viewer.MouseMove += viewer_MouseMove;
this.button1.Click += button_Click;
this.button2.Click += button_Click;
this.button3.Click += button_Click;
this.button4.Click += button_Click;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 뷰어 마우스 왼쪽 버튼 DOWN 처리하기 - viewer_MouseLeftButtonDown(sender, e)
/// <summary>
/// 뷰어 마우스 왼쪽 버튼 DOWN 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void viewer_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragStartPoint = e.GetPosition(this.viewer);
this.rotationVector.X = this.viewer.RotationX;
this.rotationVector.Y = this.viewer.RotationY;
}
#endregion
#region 뷰어 마우스 이동시 처리하기 - viewer_MouseMove(sender, e)
/// <summary>
/// 뷰어 마우스 이동시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void viewer_MouseMove(object sender, MouseEventArgs e)
{
if(e.LeftButton == MouseButtonState.Released)
{
return;
}
Vector offsetVector = Point.Subtract(e.GetPosition(this.viewer) , this.dragStartPoint) * 0.25;
this.viewer.RotationY = this.rotationVector.Y + offsetVector.X;
this.viewer.RotationX = this.rotationVector.X + offsetVector.Y;
}
#endregion
#region 버튼 클릭시 처리하기 - button_Click(sender, e)
/// <summary>
/// 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void button_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
string imageFileName = null;
switch(button.Name)
{
case "button1" : imageFileName = "sample1.jpg"; break;
case "button2" : imageFileName = "sample2.jpg"; break;
case "button3" : imageFileName = "sample3.jpg"; break;
case "button4" : imageFileName = "sample4.jpg"; break;
}
Uri uri = new Uri($"pack://application:,,,/{imageFileName}");
BitmapImage bitmapImage = new BitmapImage(uri);
this.viewer.PanoramaImage = bitmapImage;
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] MarkupExtension 클래스 : 마크업 확장 사용하기 (0) | 2020.12.15 |
---|---|
[C#/WPF] Image 클래스 : LayoutUpdated 이벤트를 사용해 이미지 픽셀 보정하기 (0) | 2020.12.13 |
[C#/WPF] FrameworkElement 클래스 : 장평을 설정하는 커스텀 텍스트 블럭 사용하기 (0) | 2020.12.13 |
[C#/WPF] Grid 클래스 : 환형 패널 사용하기 (0) | 2020.12.12 |
[C#/WPF] ObjectDataProvider 엘리먼트 : Colors 클래스의 색상 정적 속성 구하기 (0) | 2020.12.07 |
[C#/WPF] TabControl 클래스 : FADE IN/OUT 탭 컨트롤 사용하기 (0) | 2020.12.06 |
[C#/WPF] FrameworkElement 엘리먼트 : FocusVisualStyle 속성을 사용해 포커스 사각형 제거하기 (0) | 2020.12.05 |
[C#/WPF] UIElement 클래스 : MouseMove 이벤트를 사용해 마우스 이동시 애니메이션 설정하기 (0) | 2020.12.05 |
[C#/WPF] EventTrigger 엘리먼트 : RoutedEvent 속성을 사용해 복권 긁는 효과 사용하기 (0) | 2020.12.05 |
[C#/WPF] UIElement 클래스 : 복제하기 (0) | 2020.12.05 |
댓글을 달아 주세요