@@ -76,8 +76,6 @@ namespace ImPlot3D {
7676ImPlot3DContext* GImPlot3D = nullptr ;
7777#endif
7878
79- static ImPlot3DQuat init_rotation = ImPlot3DQuat(-0 .513269f , -0 .212596f , -0 .318184f , 0 .76819f );
80-
8179ImPlot3DContext* CreateContext () {
8280 ImPlot3DContext* ctx = IM_NEW (ImPlot3DContext)();
8381 if (GImPlot3D == nullptr )
@@ -893,7 +891,7 @@ void ComputeActiveFaces(bool* active_faces, const ImPlot3DQuat& rotation, const
893891 };
894892
895893 int num_deg = 0 ; // Check number of planes that are degenerate (seen as a line)
896- for (int i = 0 ; i < 3 ; ++i ) {
894+ for (int i = 0 ; i < 3 ; i++ ) {
897895 // Determine the active face based on the Z component
898896 if (fabs (rot_face_n[i].z ) < 0.025 ) {
899897 // If aligned with the plane, choose the min face for bottom/left
@@ -1106,7 +1104,7 @@ void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float p
11061104 }
11071105 ticker.AddTick (major, true , true , formatter, formatter_data);
11081106 }
1109- for (int i = 1 ; i < nMinor; ++i ) {
1107+ for (int i = 1 ; i < nMinor; i++ ) {
11101108 double minor = major + i * interval / nMinor;
11111109 if (range.Contains ((float )minor)) {
11121110 ticker.AddTick (minor, false , true , formatter, formatter_data);
@@ -1124,7 +1122,7 @@ void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float p
11241122}
11251123
11261124void AddTicksCustom (const double * values, const char * const labels[], int n, ImPlot3DTicker& ticker, ImPlot3DFormatter formatter, void * data) {
1127- for (int i = 0 ; i < n; ++i ) {
1125+ for (int i = 0 ; i < n; i++ ) {
11281126 if (labels != nullptr )
11291127 ticker.AddTick (values[i], false , true , labels[i]);
11301128 else
@@ -1315,7 +1313,7 @@ bool BeginPlot(const char* title_id, const ImVec2& size, ImPlot3DFlags flags) {
13151313 plot.ID = ID;
13161314 plot.JustCreated = just_created;
13171315 if (just_created) {
1318- plot.Rotation = init_rotation ;
1316+ plot.Rotation = plot. InitialRotation ;
13191317 plot.FitThisFrame = true ;
13201318 for (int i = 0 ; i < 3 ; i++) {
13211319 plot.Axes [i] = ImPlot3DAxis ();
@@ -1327,6 +1325,7 @@ bool BeginPlot(const char* title_id, const ImVec2& size, ImPlot3DFlags flags) {
13271325 plot.PreviousFlags = flags;
13281326 plot.SetupLocked = false ;
13291327 plot.OpenContextThisFrame = false ;
1328+ plot.RotationCond = ImPlot3DCond_None;
13301329
13311330 // Populate title
13321331 plot.SetTitle (title_id);
@@ -1356,9 +1355,8 @@ bool BeginPlot(const char* title_id, const ImVec2& size, ImPlot3DFlags flags) {
13561355 plot.Items .Legend .Reset ();
13571356
13581357 // Reset axes
1359- for (int i = 0 ; i < ImAxis3D_COUNT; ++i) {
1358+ for (int i = 0 ; i < ImAxis3D_COUNT; i++)
13601359 plot.Axes [i].Reset ();
1361- }
13621360
13631361 // Push frame rect clipping
13641362 ImGui::PushClipRect (plot.FrameRect .Min , plot.FrameRect .Max , true );
@@ -1455,6 +1453,17 @@ void EndPlot() {
14551453// [SECTION] Setup
14561454// -----------------------------------------------------------------------------
14571455
1456+ static const float ANIMATION_ANGULAR_VELOCITY = 2 * 3 .1415f ;
1457+
1458+ float CalcAnimationTime (ImPlot3DQuat q0, ImPlot3DQuat q1) {
1459+ // Compute the angular distance between orientations
1460+ float dot_product = ImClamp (q0.Dot (q1), -1 .0f , 1 .0f );
1461+ float angle = 2 .0f * acosf (fabsf (dot_product));
1462+
1463+ // Calculate animation time for constant the angular velocity
1464+ return angle / ANIMATION_ANGULAR_VELOCITY;
1465+ }
1466+
14581467void SetupAxis (ImAxis3D idx, const char * label, ImPlot3DAxisFlags flags) {
14591468 ImPlot3DContext& gp = *GImPlot3D;
14601469 IM_ASSERT_USER_ERROR (gp.CurrentPlot != nullptr && !gp.CurrentPlot ->SetupLocked ,
@@ -1472,7 +1481,7 @@ void SetupAxis(ImAxis3D idx, const char* label, ImPlot3DAxisFlags flags) {
14721481void SetupAxisLimits (ImAxis3D idx, double min_lim, double max_lim, ImPlot3DCond cond) {
14731482 ImPlot3DContext& gp = *GImPlot3D;
14741483 IM_ASSERT_USER_ERROR (gp.CurrentPlot != nullptr && !gp.CurrentPlot ->SetupLocked ,
1475- " SetupAxisLimits() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!" ); // get plot and axis
1484+ " SetupAxisLimits() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!" );
14761485 ImPlot3DPlot& plot = *gp.CurrentPlot ;
14771486 ImPlot3DAxis& axis = plot.Axes [idx];
14781487 if (!plot.Initialized || cond == ImPlot3DCond_Always) {
@@ -1531,6 +1540,51 @@ void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, dou
15311540 GImPlot3D->CurrentPlot ->FitThisFrame = false ;
15321541}
15331542
1543+ void SetupBoxRotation (float elevation, float azimuth, bool animate, ImPlot3DCond cond) {
1544+ // Convert angles from degrees to radians
1545+ float elev_rad = elevation * IM_PI / 180 .0f ;
1546+ float azim_rad = azimuth * IM_PI / 180 .0f ;
1547+
1548+ // Call quaternion SetupBoxRotation
1549+ SetupBoxRotation (ImPlot3DQuat::FromElAz (elev_rad, azim_rad), animate, cond);
1550+ }
1551+
1552+ void SetupBoxRotation (ImPlot3DQuat rotation, bool animate, ImPlot3DCond cond) {
1553+ ImPlot3DContext& gp = *GImPlot3D;
1554+ IM_ASSERT_USER_ERROR (gp.CurrentPlot != nullptr && !gp.CurrentPlot ->SetupLocked ,
1555+ " SetupBoxRotation() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!" );
1556+ ImPlot3DPlot& plot = *gp.CurrentPlot ;
1557+
1558+ if (!plot.Initialized || cond == ImPlot3DCond_Always) {
1559+ if (!animate) {
1560+ plot.Rotation = rotation;
1561+ plot.AnimationTime = 0 .0f ; // Force any running rotation animation to stop
1562+ } else {
1563+ plot.RotationAnimationEnd = rotation;
1564+ plot.AnimationTime = CalcAnimationTime (plot.Rotation , plot.RotationAnimationEnd );
1565+ }
1566+ plot.RotationCond = cond;
1567+ }
1568+ }
1569+
1570+ void SetupBoxInitialRotation (float elevation, float azimuth) {
1571+ // Convert angles from degrees to radians
1572+ float elev_rad = elevation * IM_PI / 180 .0f ;
1573+ float azim_rad = azimuth * IM_PI / 180 .0f ;
1574+
1575+ // Call quaternion SetupBoxInitialRotation
1576+ SetupBoxInitialRotation (ImPlot3DQuat::FromElAz (elev_rad, azim_rad));
1577+ }
1578+
1579+ void SetupBoxInitialRotation (ImPlot3DQuat rotation) {
1580+ ImPlot3DContext& gp = *GImPlot3D;
1581+ IM_ASSERT_USER_ERROR (gp.CurrentPlot != nullptr && !gp.CurrentPlot ->SetupLocked ,
1582+ " SetupBoxInitialRotation() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!" );
1583+ ImPlot3DPlot& plot = *gp.CurrentPlot ;
1584+
1585+ plot.InitialRotation = rotation;
1586+ }
1587+
15341588void SetupBoxScale (float x, float y, float z) {
15351589 ImPlot3DContext& gp = *GImPlot3D;
15361590 IM_ASSERT_USER_ERROR (gp.CurrentPlot != nullptr && !gp.CurrentPlot ->SetupLocked ,
@@ -1795,7 +1849,6 @@ ImPlot3DRay NDCRayToPlotRay(const ImPlot3DRay& ray) {
17951849// -----------------------------------------------------------------------------
17961850
17971851static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5 .0f ;
1798- static const float ANIMATION_ANGULAR_VELOCITY = 2 * 3 .1415f ;
17991852
18001853void HandleInput (ImPlot3DPlot& plot) {
18011854 ImGuiIO& IO = ImGui::GetIO ();
@@ -1962,12 +2015,12 @@ void HandleInput(ImPlot3DPlot& plot) {
19622015 plot.ContextClick = false ;
19632016
19642017 // Handle reset rotation with left mouse double click
1965- if (plot.Held && ImGui::IsMouseDoubleClicked (ImGuiMouseButton_Right)) {
2018+ if (plot.Held && ImGui::IsMouseDoubleClicked (ImGuiMouseButton_Right) && !plot. IsRotationLocked () ) {
19662019 plot.RotationAnimationEnd = plot.Rotation ;
19672020
19682021 // Calculate rotation to align the z-axis with the camera direction
19692022 if (hovered_plane == -1 ) {
1970- plot.RotationAnimationEnd = init_rotation ;
2023+ plot.RotationAnimationEnd = plot. InitialRotation ;
19712024 } else {
19722025 // Compute plane normal
19732026 ImPlot3DPoint axis_normal = ImPlot3DPoint (0 .0f , 0 .0f , 0 .0f );
@@ -2005,7 +2058,7 @@ void HandleInput(ImPlot3DPlot& plot) {
20052058
20062059 // Find the candidate with the maximum dot product
20072060 AxisAlignment* best_candidate = &candidates[0 ];
2008- for (int i = 1 ; i < 4 ; ++i ) {
2061+ for (int i = 1 ; i < 4 ; i++ ) {
20092062 if (candidates[i].dot > best_candidate->dot ) {
20102063 best_candidate = &candidates[i];
20112064 }
@@ -2017,16 +2070,12 @@ void HandleInput(ImPlot3DPlot& plot) {
20172070 }
20182071 }
20192072
2020- // Compute the angular distance between current and target rotation
2021- float dot_product = ImClamp (plot.Rotation .Dot (plot.RotationAnimationEnd ), -1 .0f , 1 .0f );
2022- float angle = 2 .0f * acosf (fabsf (dot_product));
2023-
2024- // Calculate animation time for constant the angular velocity
2025- plot.AnimationTime = angle / ANIMATION_ANGULAR_VELOCITY;
2073+ // Calculate animation time
2074+ plot.AnimationTime = CalcAnimationTime (plot.Rotation , plot.RotationAnimationEnd );
20262075 }
20272076
20282077 // Handle rotation with left mouse dragging
2029- if (plot.Held && ImGui::IsMouseDown (ImGuiMouseButton_Right)) {
2078+ if (plot.Held && ImGui::IsMouseDown (ImGuiMouseButton_Right) && !plot. IsRotationLocked () ) {
20302079 ImVec2 delta (IO.MouseDelta .x , IO.MouseDelta .y );
20312080
20322081 // Map delta to rotation angles (in radians)
@@ -2424,7 +2473,7 @@ ImPlot3DColormap AddColormap(const char* name, const ImVec4* colormap, int size,
24242473 IM_ASSERT_USER_ERROR (gp.ColormapData .GetIndex (name) == -1 , " The colormap name has already been used!" );
24252474 ImVector<ImU32> buffer;
24262475 buffer.resize (size);
2427- for (int i = 0 ; i < size; ++i )
2476+ for (int i = 0 ; i < size; i++ )
24282477 buffer[i] = ImGui::ColorConvertFloat4ToU32 (colormap[i]);
24292478 return gp.ColormapData .Append (name, buffer.Data , size, qual);
24302479}
@@ -2868,6 +2917,16 @@ ImPlot3DQuat ImPlot3DQuat::FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3
28682917 return q;
28692918}
28702919
2920+ ImPlot3DQuat ImPlot3DQuat::FromElAz (float elevation, float azimuth) {
2921+ // Create quaternions for azimuth and elevation
2922+ ImPlot3DQuat azimuth_quat (azimuth, ImPlot3DPoint (0 .0f , 0 .0f , 1 .0f )); // Rotate around Z-axis
2923+ ImPlot3DQuat elevation_quat (elevation, ImPlot3DPoint (1 .0f , 0 .0f , 0 .0f )); // Rotate around X-axis
2924+ ImPlot3DQuat zero_quat (-IM_PI / 2 , ImPlot3DPoint (1 .0f , 0 .0f , 0 .0f )); // Rotate to zero azimuth/elevation orientation
2925+
2926+ // Combine rotations
2927+ return elevation_quat * zero_quat * azimuth_quat;
2928+ }
2929+
28712930float ImPlot3DQuat::Length () const {
28722931 return ImSqrt (x * x + y * y + z * z + w * w);
28732932}
0 commit comments