1
1
/*
2
- * Copyright (c) 2023 , Peter Abeles. All Rights Reserved.
2
+ * Copyright (c) 2024 , Peter Abeles. All Rights Reserved.
3
3
*
4
4
* This file is part of BoofCV (http://boofcv.org).
5
5
*
21
21
import boofcv .alg .geo .PerspectiveOps ;
22
22
import boofcv .struct .calib .CameraPinhole ;
23
23
import georegression .geometry .ConvertRotation3D_F64 ;
24
- import georegression .geometry .GeometryMath_F64 ;
25
24
import georegression .metric .UtilAngle ;
26
25
import georegression .struct .EulerType ;
27
26
import georegression .struct .point .Point2D_F64 ;
28
27
import georegression .struct .point .Point3D_F64 ;
29
- import georegression .struct .point .Vector3D_F64 ;
30
28
import georegression .struct .se .Se3_F64 ;
31
29
import lombok .Getter ;
32
- import org .ejml .data .DMatrixRMaj ;
33
- import org .ejml .dense .row .CommonOps_DDRM ;
34
30
35
31
/**
36
32
* Contains the mathematics for controlling a camera by orbiting around a point in 3D space
@@ -44,54 +40,73 @@ public class OrbitAroundPoint {
44
40
/** Transform from world to camera view reference frames */
45
41
Se3_F64 worldToView = new Se3_F64 ();
46
42
47
- DMatrixRMaj localRotation = new DMatrixRMaj (3 , 3 );
48
- DMatrixRMaj rotationAroundTarget = new DMatrixRMaj (3 , 3 );
49
- DMatrixRMaj tmp = new DMatrixRMaj (3 , 3 );
50
-
51
- // Translation applied after the orbit has been done
52
- Vector3D_F64 translateWorld = new Vector3D_F64 ();
53
-
54
- // Point it's orbiting around
55
- Point3D_F64 targetPoint = new Point3D_F64 ();
56
-
57
- // Adjustment applied to distance from target point in final transform
58
- double radiusScale = 1.0 ;
59
-
60
- Point3D_F64 cameraLoc = new Point3D_F64 ();
43
+ // Point it's orbiting around in world coordinates
44
+ private Point3D_F64 targetWorld = new Point3D_F64 ();
61
45
62
46
// Storage for normalized image coordinates
63
47
Point2D_F64 norm1 = new Point2D_F64 ();
64
48
Point2D_F64 norm2 = new Point2D_F64 ();
65
49
50
+ Point3D_F64 targetInView = new Point3D_F64 ();
51
+ Point3D_F64 targetInOld = new Point3D_F64 ();
52
+ Se3_F64 viewToPoint = new Se3_F64 ();
53
+ Se3_F64 viewToPoint2 = new Se3_F64 ();
54
+ Se3_F64 worldToPoint = new Se3_F64 ();
55
+ Se3_F64 pointToRot = new Se3_F64 ();
56
+ Se3_F64 oldToNew = new Se3_F64 ();
57
+
66
58
public OrbitAroundPoint () {
67
59
resetView ();
68
60
}
69
61
62
+ public void setTarget ( double x , double y , double z ) {
63
+ targetWorld .setTo (x , y , z );
64
+
65
+ updateAfterExternalChange ();
66
+ }
67
+
70
68
public void resetView () {
71
- radiusScale = 1.0 ;
72
- translateWorld .zero ();
73
- CommonOps_DDRM .setIdentity (rotationAroundTarget );
69
+ worldToView .reset ();
70
+
71
+ // See if the principle point and the target are on top of each other
72
+ updateAfterExternalChange ();
73
+ }
74
+
75
+ /**
76
+ * After an external change to the transform or target, make sure it's pointed at the target and a reasonable
77
+ * distance.
78
+ */
79
+ private void updateAfterExternalChange () {
80
+ // See if the principle point and the target are on top of each other
81
+ if (Math .abs (worldToView .T .distance (targetWorld )) < 1e-16 ) {
82
+ // back the camera off a little bit
83
+ worldToView .T .z += 0.1 ;
84
+ }
85
+
86
+ pointAtTarget ();
74
87
}
75
88
76
- public void updateTransform () {
77
- // Compute location of camera principle point with no rotation in target point reference frame
78
- cameraLoc .x = -targetPoint .x *radiusScale ;
79
- cameraLoc .y = -targetPoint .y *radiusScale ;
80
- cameraLoc .z = -targetPoint .z *radiusScale ;
81
-
82
- // Apply rotation
83
- GeometryMath_F64 .mult (rotationAroundTarget , cameraLoc , cameraLoc );
84
-
85
- // Compute the full transform
86
- worldToView .T .setTo (
87
- cameraLoc .x + targetPoint .x + translateWorld .x ,
88
- cameraLoc .y + targetPoint .y + translateWorld .y ,
89
- cameraLoc .z + targetPoint .z + translateWorld .z );
90
- worldToView .R .setTo (rotationAroundTarget );
89
+ /**
90
+ * Points the camera at the target point
91
+ */
92
+ private void pointAtTarget () {
93
+ oldToNew .reset ();
94
+
95
+ worldToView .transform (targetWorld , targetInView );
96
+ PerspectiveOps .pointAt (targetInView .x , targetInView .y , targetInView .z , oldToNew .R );
97
+
98
+ Se3_F64 tmp = pointToRot ;
99
+ worldToView .concatInvert (oldToNew , tmp );
100
+ worldToView .setTo (tmp );
91
101
}
92
102
93
103
public void mouseWheel ( double ticks , double scale ) {
94
- radiusScale = Math .max (0.005 , radiusScale *(1.0 + 0.02 *ticks *scale ));
104
+ worldToView .transform (targetWorld , targetInView );
105
+
106
+ // How much it will move towards or away from the target
107
+ worldToView .T .z += targetInView .norm ()*0.02 *ticks *scale ;
108
+
109
+ // Because it's moving towards the point it won't need to adjust its angle
95
110
}
96
111
97
112
public void mouseDragRotate ( double x0 , double y0 , double x1 , double y1 ) {
@@ -107,31 +122,29 @@ public void mouseDragRotate( double x0, double y0, double x1, double y1 ) {
107
122
double rotX = UtilAngle .minus (Math .atan (norm1 .x ), Math .atan (norm2 .x ));
108
123
double rotY = UtilAngle .minus (Math .atan (norm1 .y ), Math .atan (norm2 .y ));
109
124
110
- // Set the local rotation
111
- ConvertRotation3D_F64 .eulerToMatrix (EulerType .XYZ , -rotY , rotX , 0 , localRotation );
112
-
113
- // Update the global rotation
114
- CommonOps_DDRM .mult (localRotation , rotationAroundTarget , tmp );
115
- rotationAroundTarget .setTo (tmp );
125
+ applyLocalEuler (rotX , -rotY , 0.0 );
116
126
}
117
127
118
128
/**
119
- * Uses mouse drag motion to translate the view
129
+ * Applies a X Y Z Euler rotation in the point's reference frame so that it will appear to be rotating
130
+ * around that point
120
131
*/
121
- public void mouseDragTranslate ( double x0 , double y0 , double x1 , double y1 ) {
122
- // do nothing if the camera isn't configured yet
123
- if (camera .fx == 0.0 || camera .fy == 0.0 )
124
- return ;
132
+ private void applyLocalEuler ( double rotX , double rotY , double rotZ ) {
133
+ pointToRot .reset ();
125
134
126
- // convert into normalize image coordinates
127
- PerspectiveOps . convertPixelToNorm ( camera , x0 , y0 , norm1 );
128
- PerspectiveOps . convertPixelToNorm ( camera , x1 , y1 , norm2 );
135
+ worldToView . transform ( targetWorld , targetInOld );
136
+
137
+ viewToPoint . T . setTo (- targetInOld . x , - targetInOld . y , - targetInOld . z );
129
138
130
- // Figure out the distance along the projection at the plane at the distance of the target point
131
- double z = targetPoint .plus (translateWorld ).norm ()*radiusScale ;
139
+ worldToView .concat (viewToPoint , worldToPoint );
132
140
133
- translateWorld .x += (norm2 .x - norm1 .x )*z ;
134
- translateWorld .y += (norm2 .y - norm1 .y )*z ;
141
+ // Set the local rotation
142
+ ConvertRotation3D_F64 .eulerToMatrix (EulerType .XYZ , rotY , rotX , rotZ , pointToRot .R );
143
+
144
+ viewToPoint .concat (pointToRot , viewToPoint2 );
145
+ worldToPoint .concatInvert (viewToPoint2 , worldToView );
146
+
147
+ pointAtTarget ();
135
148
}
136
149
137
150
/**
@@ -147,14 +160,11 @@ public void mouseDragZoomRoll( double x0, double y0, double x1, double y1 ) {
147
160
PerspectiveOps .convertPixelToNorm (camera , x1 , y1 , norm2 );
148
161
149
162
// Zoom in and out using the mouse
150
- double z = targetPoint . plus ( translateWorld ). norm ()* radiusScale ;
151
- translateWorld . z += (norm2 .y - norm1 .y )*z ;
163
+ worldToView . transform ( targetWorld , targetInView ) ;
164
+ worldToView . T . z += (norm2 .y - norm1 .y )*targetInView . norm () ;
152
165
153
- // Perform roll around the z-axis
166
+ // Roll the camera
154
167
double rotX = UtilAngle .minus (Math .atan (norm1 .x ), Math .atan (norm2 .x ));
155
- ConvertRotation3D_F64 .eulerToMatrix (EulerType .XYZ , 0 , 0 , -rotX , localRotation );
156
- CommonOps_DDRM .mult (localRotation , rotationAroundTarget , tmp );
157
- rotationAroundTarget .setTo (tmp );
158
-
168
+ applyLocalEuler (0 , 0 , rotX );
159
169
}
160
170
}
0 commit comments