Skip to content

Commit 660c85f

Browse files
authored
Re-implmenet zoom extents in sharpdx versions. (helix-toolkit#2003)
* Properly implmenet zoom extents in sharpdx versions. * Update demo.
1 parent b2dbadb commit 660c85f

File tree

10 files changed

+280
-55
lines changed

10 files changed

+280
-55
lines changed

Source/Examples/WPF.SharpDX/SimpleDemo/MainViewModel.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ public MainViewModel()
7272
SubTitle = "WPF & SharpDX";
7373

7474
// camera setup
75-
Camera = new PerspectiveCamera {
75+
Camera = new OrthographicCamera {
7676
Position = new Point3D(3, 3, 5),
7777
LookDirection = new Vector3D(-3, -3, -5),
7878
UpDirection = new Vector3D(0, 1, 0),
79-
FarPlaneDistance = 5000000
79+
FarPlaneDistance = 50000
8080
};
8181

8282
// setup lighting

Source/HelixToolkit.SharpDX.Core/Extensions/CameraExtension.cs

+25-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System;
33
namespace HelixToolkit.SharpDX.Core
44
{
5-
using Controls;
5+
using Controls;
66
using Cameras;
77
public static class CameraExtension
88
{
@@ -23,7 +23,7 @@ public static void LookAt(this CameraCore camera, Vector3 target, float animatio
2323
if (camera is ProjectionCameraCore projectionCamera)
2424
{
2525
LookAt(camera, target, projectionCamera.LookDirection, animationTime);
26-
}
26+
}
2727
}
2828

2929
/// <summary>
@@ -128,9 +128,9 @@ public static void AnimateWidth(this OrthographicCameraCore camera, float newWid
128128
/// </param>
129129
public static void ZoomToRectangle(this CameraCore camera, ViewportCore viewport, RectangleF zoomRectangle)
130130
{
131-
if(camera is ProjectionCameraCore pcam)
131+
if (camera is ProjectionCameraCore pcam)
132132
{
133-
if(viewport.UnProject(new Vector2(zoomRectangle.Top, zoomRectangle.Left), out var topLeftRay)
133+
if (viewport.UnProject(new Vector2(zoomRectangle.Top, zoomRectangle.Left), out var topLeftRay)
134134
&& viewport.UnProject(new Vector2(zoomRectangle.Top, zoomRectangle.Right), out var topRightRay)
135135
&& viewport.UnProject(
136136
new Vector2(
@@ -143,7 +143,7 @@ public static void ZoomToRectangle(this CameraCore camera, ViewportCore viewport
143143
u.Normalize();
144144
v.Normalize();
145145
w.Normalize();
146-
if(camera is PerspectiveCameraCore perspectiveCamera)
146+
if (camera is PerspectiveCameraCore perspectiveCamera)
147147
{
148148
var distance = pcam.LookDirection.Length();
149149

@@ -154,12 +154,12 @@ public static void ZoomToRectangle(this CameraCore camera, ViewportCore viewport
154154
var newTarget = newPosition + newLookDirection;
155155
LookAt(pcam, newTarget, newLookDirection, 200);
156156
}
157-
else if(camera is OrthographicCameraCore orthographicCamera)
157+
else if (camera is OrthographicCameraCore orthographicCamera)
158158
{
159159
orthographicCamera.Width *= zoomRectangle.Width / viewport.ViewportRectangle.Width;
160160
var oldTarget = pcam.Position + pcam.LookDirection;
161161
var distance = pcam.LookDirection.Length();
162-
162+
163163
if (centerRay.PlaneIntersection(oldTarget, w, out var newTarget))
164164
{
165165
orthographicCamera.LookDirection = w * distance;
@@ -181,7 +181,7 @@ public static void Reset(this CameraCore camera)
181181
if (camera is PerspectiveCameraCore projectionCamera)
182182
{
183183
Reset(projectionCamera);
184-
}
184+
}
185185
else if (camera is OrthographicCameraCore ocamera)
186186
{
187187
Reset(ocamera);
@@ -245,13 +245,6 @@ public static void ZoomExtents(
245245
this CameraCore camera, ViewportCore viewport, float animationTime = 0)
246246
{
247247
var bounds = viewport.FindBoundsInternal();
248-
var diagonal = bounds.Maximum-bounds.Minimum;
249-
250-
if (diagonal.LengthSquared().Equals(0))
251-
{
252-
return;
253-
}
254-
255248
ZoomExtents(camera, viewport, bounds, animationTime);
256249
}
257250

@@ -271,12 +264,25 @@ public static void ZoomExtents(
271264
/// The animation time.
272265
/// </param>
273266
public static void ZoomExtents(
274-
this CameraCore camera, ViewportCore viewport, BoundingBox bounds, float animationTime = 0)
267+
this CameraCore camera, ViewportCore viewport, global::SharpDX.BoundingBox bounds, float animationTime = 0)
275268
{
276269
var diagonal = bounds.Maximum - bounds.Minimum;
277-
var center = bounds.Center;
278-
float radius = diagonal.Length() * 0.5f;
279-
ZoomExtents(camera, viewport, center, radius, animationTime);
270+
271+
if (diagonal.LengthSquared().Equals(0))
272+
{
273+
return;
274+
}
275+
if (camera is PerspectiveCameraCore pCore)
276+
{
277+
pCore.ZoomExtents((float)(viewport.ActualWidth / viewport.ActualHeight), bounds, out var pos, out var look, out var up);
278+
pCore.AnimateTo(pos, look, up, animationTime);
279+
}
280+
else if (camera is OrthographicCameraCore oCore)
281+
{
282+
oCore.ZoomExtents((float)(viewport.ActualWidth / viewport.ActualHeight), bounds, out var pos, out var look, out var up, out var width);
283+
oCore.AnimateWidth(width, animationTime);
284+
oCore.AnimateTo(pos, look, up, animationTime);
285+
}
280286
}
281287

282288
/// <summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
using SharpDX;
2+
using System;
3+
#if !NETFX_CORE
4+
namespace HelixToolkit.Wpf.SharpDX
5+
#else
6+
#if CORE
7+
namespace HelixToolkit.SharpDX.Core
8+
#else
9+
namespace HelixToolkit.UWP
10+
#endif
11+
#endif
12+
{
13+
using Cameras;
14+
public static class CameraCoreExtensions
15+
{
16+
public static BoundingFrustum CreateFrustum(this CameraCore camera, float aspectRatio)
17+
{
18+
return new BoundingFrustum(camera.CreateViewMatrix() * camera.CreateProjectionMatrix(aspectRatio));
19+
}
20+
21+
// Returns whether or not the given point is the outermost point in the given direction among all points of the bounds
22+
private static bool IsOutermostPointInDirection(int pointIndex, ref Vector3 direction, Vector3[] corners)
23+
{
24+
Vector3 point = corners[pointIndex];
25+
for (int i = 0; i < corners.Length; i++)
26+
{
27+
if (i != pointIndex && Vector3.Dot(direction, corners[i] - point) > 0)
28+
return false;
29+
}
30+
31+
return true;
32+
}
33+
34+
// Credit: http://wiki.unity3d.com/index.php/3d_Math_functions
35+
// Returns the edge points of the closest line segment between 2 lines
36+
private static void FindClosestPointsOnTwoLines(ref Ray line1, ref Ray line2, out Vector3 closestPointLine1, out Vector3 closestPointLine2)
37+
{
38+
Vector3 line1Direction = line1.Direction;
39+
Vector3 line2Direction = line2.Direction;
40+
41+
float a = Vector3.Dot(line1Direction, line1Direction);
42+
float b = Vector3.Dot(line1Direction, line2Direction);
43+
float e = Vector3.Dot(line2Direction, line2Direction);
44+
45+
float d = a * e - b * b;
46+
47+
Vector3 r = line1.Position - line2.Position;
48+
float c = Vector3.Dot(line1Direction, r);
49+
float f = Vector3.Dot(line2Direction, r);
50+
51+
float s = (b * f - c * e) / d;
52+
float t = (a * f - c * b) / d;
53+
54+
closestPointLine1 = line1.Position + line1Direction * s;
55+
closestPointLine2 = line2.Position + line2Direction * t;
56+
}
57+
/// <summary>
58+
/// Ref: https://github.com/yasirkula/UnityRuntimePreviewGenerator/blob/7a3b44b07949f712010b680b9f2c499e5aa2ebc1/Plugins/RuntimePreviewGenerator/RuntimePreviewGenerator.cs#L347
59+
/// </summary>
60+
/// <param name="camera"></param>
61+
/// <param name="aspectRatio"></param>
62+
/// <param name="boundingBox"></param>
63+
/// <param name="position"></param>
64+
/// <param name="lookDir"></param>
65+
/// <param name="upDir"></param>
66+
public static void ZoomExtents(this PerspectiveCameraCore camera, float aspectRatio, BoundingBox boundingBox, out Vector3 position, out Vector3 lookDir, out Vector3 upDir)
67+
{
68+
var cameraDir = Vector3.Normalize(camera.LookDirection);
69+
var cameraUp = Vector3.Normalize(camera.UpDirection);
70+
var cameraRight = Vector3.Cross(cameraDir, cameraUp);
71+
cameraUp = Vector3.Cross(cameraRight, cameraDir);
72+
73+
var corners = boundingBox.GetCorners();
74+
75+
var frustum = camera.CreateFrustum(aspectRatio);
76+
var leftNormal = -frustum.Left.Normal;
77+
var rightNormal = -frustum.Right.Normal;
78+
var topNormal = -frustum.Top.Normal;
79+
var bottomNormal = -frustum.Bottom.Normal;
80+
81+
int leftMostPoint = -1, rightMostPoint = -1, topMostPoint = -1, bottomMostPoint = -1;
82+
for (int i = 0; i < corners.Length; i++)
83+
{
84+
if (leftMostPoint < 0 && IsOutermostPointInDirection(i, ref leftNormal, corners))
85+
{
86+
leftMostPoint = i;
87+
}
88+
if (rightMostPoint < 0 && IsOutermostPointInDirection(i, ref rightNormal, corners))
89+
{
90+
rightMostPoint = i;
91+
}
92+
if (topMostPoint < 0 && IsOutermostPointInDirection(i, ref topNormal, corners))
93+
{
94+
topMostPoint = i;
95+
}
96+
if (bottomMostPoint < 0 && IsOutermostPointInDirection(i, ref bottomNormal, corners))
97+
{
98+
bottomMostPoint = i;
99+
}
100+
}
101+
102+
var plane1 = new Plane(corners[leftMostPoint], leftNormal);
103+
var plane2 = new Plane(corners[rightMostPoint], rightNormal);
104+
PlaneExtensions.PlaneIntersectsPlane(ref plane1, ref plane2, out var horizontalIntersection);
105+
plane1 = new Plane(corners[topMostPoint], topNormal);
106+
plane2 = new Plane(corners[bottomMostPoint], bottomNormal);
107+
PlaneExtensions.PlaneIntersectsPlane(ref plane1, ref plane2, out var verticalIntersection);
108+
FindClosestPointsOnTwoLines(ref horizontalIntersection, ref verticalIntersection, out var closestPointLine1, out var closestPointLine2);
109+
position = Vector3.Dot(closestPointLine1 - closestPointLine2, cameraDir) < 0 ? closestPointLine1 : closestPointLine2;
110+
upDir = cameraUp;
111+
var boundPlane = new Plane(boundingBox.Center, cameraDir);
112+
var lookRay = new Ray(position, cameraDir);
113+
boundPlane.Intersects(ref lookRay, out float dist);
114+
lookDir = cameraDir * dist;
115+
}
116+
/// <summary>
117+
/// Ref: https://github.com/yasirkula/UnityRuntimePreviewGenerator/blob/7a3b44b07949f712010b680b9f2c499e5aa2ebc1/Plugins/RuntimePreviewGenerator/RuntimePreviewGenerator.cs#L347
118+
/// </summary>
119+
/// <param name="camera"></param>
120+
/// <param name="aspectRatio"></param>
121+
/// <param name="boundingBox"></param>
122+
/// <param name="position"></param>
123+
/// <param name="lookDir"></param>
124+
/// <param name="upDir"></param>
125+
/// <param name="width"></param>
126+
public static void ZoomExtents(this OrthographicCameraCore camera, float aspectRatio, BoundingBox boundingBox, out Vector3 position, out Vector3 lookDir, out Vector3 upDir, out float width)
127+
{
128+
float minX = float.PositiveInfinity, minY = float.PositiveInfinity, maxX = float.NegativeInfinity, maxY = float.NegativeInfinity;
129+
var corners = boundingBox.GetCorners();
130+
var view = camera.CreateViewMatrix();
131+
foreach (var p in corners)
132+
{
133+
var local = Vector3.TransformCoordinate(p, view);
134+
minX = Math.Min(minX, local.X);
135+
minY = Math.Min(minY, local.Y);
136+
maxX = Math.Max(maxX, local.X);
137+
maxY = Math.Max(maxY, local.Y);
138+
}
139+
width = aspectRatio > 1 ? Math.Max((maxX - minX), (maxY - minY) * aspectRatio) : Math.Max((maxX - minX) / aspectRatio, maxY - minY);
140+
position = boundingBox.Center - camera.LookDirection.Normalized() * width;
141+
lookDir = camera.LookDirection.Normalized() * width;
142+
upDir = camera.UpDirection;
143+
}
144+
}
145+
}

Source/HelixToolkit.SharpDX.Shared/Extensions/IViewportExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace HelixToolkit.UWP
1515
{
1616
using Cameras;
1717
using Model.Scene;
18+
1819
/// <summary>
1920
///
2021
/// </summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Linq;
3+
using System.Collections.Generic;
4+
using System.Runtime.CompilerServices;
5+
using SharpDX;
6+
#if !NETFX_CORE
7+
namespace HelixToolkit.Wpf.SharpDX
8+
#else
9+
#if CORE
10+
namespace HelixToolkit.SharpDX.Core
11+
#else
12+
namespace HelixToolkit.UWP
13+
#endif
14+
#endif
15+
{
16+
public static class PlaneExtensions
17+
{
18+
public static bool PlaneIntersectsPlane(ref Plane p1, ref Plane p2, out Ray intersection)
19+
{
20+
var dir = Vector3.Cross(p1.Normal, p2.Normal);
21+
float det = Vector3.Dot(dir, dir);
22+
if (Math.Abs(det) > float.Epsilon)
23+
{
24+
var p = (Vector3.Cross(dir, p2.Normal) * p1.D + Vector3.Cross(p1.Normal, dir) * p2.D) / det;
25+
intersection = new Ray(p, dir);
26+
return true;
27+
}
28+
intersection = default;
29+
return false;
30+
}
31+
}
32+
}

Source/HelixToolkit.SharpDX.Shared/HelixToolkit.SharpDX.Shared.projitems

+2
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,14 @@
9393
<Compile Include="$(MSBuildThisFileDirectory)Extensions\BoundingBoxExtensions.cs" />
9494
<Compile Include="$(MSBuildThisFileDirectory)Extensions\BoundingFrustumExtensions.cs" />
9595
<Compile Include="$(MSBuildThisFileDirectory)Extensions\BoundingSphereExtensions.cs" />
96+
<Compile Include="$(MSBuildThisFileDirectory)extensions\CameraCoreExtensions.cs" />
9697
<Compile Include="$(MSBuildThisFileDirectory)Extensions\CollectionExtensions.cs" />
9798
<Compile Include="$(MSBuildThisFileDirectory)Extensions\Color4Extensions.cs" />
9899
<Compile Include="$(MSBuildThisFileDirectory)Extensions\DataStreamExtension.cs" />
99100
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IRenderMetricesExtensions.cs" />
100101
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IViewportExtensions.cs" />
101102
<Compile Include="$(MSBuildThisFileDirectory)Extensions\MatrixExtensions.cs" />
103+
<Compile Include="$(MSBuildThisFileDirectory)extensions\PlaneExtensions.cs" />
102104
<Compile Include="$(MSBuildThisFileDirectory)Extensions\PointerSizeHelpers.cs" />
103105
<Compile Include="$(MSBuildThisFileDirectory)Extensions\RayExtensions.cs" />
104106
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SceneNodeExtensions.cs" />

Source/HelixToolkit.UWP.Shared/Controls/CameraExtensions.cs

+25-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ namespace HelixToolkit.WinUI
1212
namespace HelixToolkit.UWP
1313
#endif
1414
{
15+
#if !COREWPF
16+
#if WINUI
17+
using SharpDX.Core.Cameras;
18+
#else
19+
using Cameras;
20+
#endif
21+
#endif
1522
/// <summary>
1623
///
1724
/// </summary>
@@ -386,12 +393,7 @@ public static void ZoomExtents(
386393
{
387394
var bounds = viewport.FindBoundsInternal();
388395

389-
if (bounds.Maximum.IsUndefined() || bounds.Maximum == bounds.Minimum)
390-
{
391-
return;
392-
}
393-
394-
ZoomExtents(camera as ProjectionCamera, viewport, bounds, animationTime);
396+
ZoomExtents(camera, viewport, bounds, animationTime);
395397
}
396398

397399
/// <summary>
@@ -410,17 +412,25 @@ public static void ZoomExtents(
410412
/// The animation time.
411413
/// </param>
412414
public static void ZoomExtents(
413-
this Camera camera, Viewport3DX viewport, BoundingBox bounds, double animationTime = 0)
415+
this Camera camera, Viewport3DX viewport, global::SharpDX.BoundingBox bounds, double animationTime = 0)
414416
{
415-
if (!(camera is ProjectionCamera projectionCamera))
417+
var diagonal = bounds.Maximum - bounds.Minimum;
418+
419+
if (diagonal.LengthSquared().Equals(0))
416420
{
417421
return;
418422
}
419-
420-
var diagonal = bounds.Maximum - bounds.Minimum;
421-
var center = (bounds.Maximum + bounds.Minimum) / 2 + (diagonal * 0.5f);
422-
var radius = diagonal.Length() * 0.5f;
423-
ZoomExtents(projectionCamera, viewport, center, radius, animationTime);
423+
if (camera is PerspectiveCamera p && camera.CameraInternal is PerspectiveCameraCore pCore)
424+
{
425+
pCore.ZoomExtents((float)(viewport.ActualWidth / viewport.ActualHeight), bounds, out var pos, out var look, out var up);
426+
p.AnimateTo(pos, look, up, animationTime);
427+
}
428+
else if (camera is OrthographicCamera orth && camera.CameraInternal is OrthographicCameraCore oCore)
429+
{
430+
oCore.ZoomExtents((float)(viewport.ActualWidth / viewport.ActualHeight), bounds, out var pos, out var look, out var up, out var width);
431+
orth.AnimateWidth(width, animationTime);
432+
orth.AnimateTo(pos, look, up, animationTime);
433+
}
424434
}
425435

426436
/// <summary>
@@ -452,9 +462,9 @@ public static void ZoomExtents(
452462
// var target = Camera.Position + Camera.LookDirection;
453463
if (camera is PerspectiveCamera pcam)
454464
{
455-
double disth = radius / Math.Tan(0.75 * pcam.FieldOfView * Math.PI / 180);
465+
double disth = radius / Math.Tan(0.5 * pcam.FieldOfView * Math.PI / 180);
456466
double vfov = pcam.FieldOfView / viewport.ActualWidth * viewport.ActualHeight;
457-
double distv = radius / Math.Tan(0.75 * vfov * Math.PI / 180);
467+
double distv = radius / Math.Tan(0.5 * vfov * Math.PI / 180);
458468

459469
var dist = (float)Math.Max(disth, distv);
460470
var dir = projectionCamera.LookDirection;

0 commit comments

Comments
 (0)