mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-07 06:57:36 -06:00
QoL: Add auto perspective (#8312)
* ImGuizmo: Comment out unused code * 3DNav: Avoid gimbal lock by using polar coordinates * 3DNav: Make sure top and bottom are oriented correctly * Add auto perspective * Add options --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
1558edf827
commit
345ab82f91
7 changed files with 173 additions and 87 deletions
|
@ -31,6 +31,10 @@
|
||||||
#include <imgui/imgui_internal.h>
|
#include <imgui/imgui_internal.h>
|
||||||
#include "ImGuizmo.h"
|
#include "ImGuizmo.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <math.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -678,16 +682,19 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
|
|
||||||
struct Context
|
struct Context
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false)
|
Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
ImDrawList* mDrawList;
|
ImDrawList* mDrawList;
|
||||||
Style mStyle;
|
Style mStyle;
|
||||||
|
#if 0
|
||||||
MODE mMode;
|
MODE mMode;
|
||||||
|
#endif
|
||||||
matrix_t mViewMat;
|
matrix_t mViewMat;
|
||||||
matrix_t mProjectionMat;
|
matrix_t mProjectionMat;
|
||||||
|
#if 0
|
||||||
matrix_t mModel;
|
matrix_t mModel;
|
||||||
matrix_t mModelLocal; // orthonormalized model
|
matrix_t mModelLocal; // orthonormalized model
|
||||||
matrix_t mModelInverse;
|
matrix_t mModelInverse;
|
||||||
|
@ -702,9 +709,10 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
vec_t mCameraRight;
|
vec_t mCameraRight;
|
||||||
vec_t mCameraDir;
|
vec_t mCameraDir;
|
||||||
vec_t mCameraUp;
|
vec_t mCameraUp;
|
||||||
|
#endif
|
||||||
vec_t mRayOrigin;
|
vec_t mRayOrigin;
|
||||||
vec_t mRayVector;
|
vec_t mRayVector;
|
||||||
|
#if 0
|
||||||
float mRadiusSquareCenter;
|
float mRadiusSquareCenter;
|
||||||
ImVec2 mScreenSquareCenter;
|
ImVec2 mScreenSquareCenter;
|
||||||
ImVec2 mScreenSquareMin;
|
ImVec2 mScreenSquareMin;
|
||||||
|
@ -715,9 +723,10 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
|
|
||||||
bool mbUsing;
|
bool mbUsing;
|
||||||
bool mbEnable;
|
bool mbEnable;
|
||||||
|
#endif
|
||||||
bool mbMouseOver;
|
bool mbMouseOver;
|
||||||
bool mReversed; // reversed projection matrix
|
bool mReversed; // reversed projection matrix
|
||||||
|
#if 0
|
||||||
// translation
|
// translation
|
||||||
vec_t mTranslationPlan;
|
vec_t mTranslationPlan;
|
||||||
vec_t mTranslationPlanOrigin;
|
vec_t mTranslationPlanOrigin;
|
||||||
|
@ -773,11 +782,13 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
|
|
||||||
bool mAllowAxisFlip = true;
|
bool mAllowAxisFlip = true;
|
||||||
float mGizmoSizeClipSpace = 0.1f;
|
float mGizmoSizeClipSpace = 0.1f;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static Context gContext;
|
static Context gContext;
|
||||||
|
|
||||||
static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) };
|
static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) };
|
||||||
|
#if 0
|
||||||
static const char* translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f",
|
static const char* translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f",
|
||||||
"Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f",
|
"Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f",
|
||||||
"X : %5.3f Y : %5.3f Z : %5.3f" };
|
"X : %5.3f Y : %5.3f Z : %5.3f" };
|
||||||
|
@ -795,7 +806,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion);
|
static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion);
|
||||||
static int GetRotateType(OPERATION op);
|
static int GetRotateType(OPERATION op);
|
||||||
static int GetScaleType(OPERATION op);
|
static int GetScaleType(OPERATION op);
|
||||||
|
#endif
|
||||||
Style& GetStyle()
|
Style& GetStyle()
|
||||||
{
|
{
|
||||||
return gContext.mStyle;
|
return gContext.mStyle;
|
||||||
|
@ -807,7 +818,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
return ImGui::ColorConvertFloat4ToU32(gContext.mStyle.Colors[idx]);
|
return ImGui::ColorConvertFloat4ToU32(gContext.mStyle.Colors[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight))
|
static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, ImVec2 position = ImVec2(0, 0), ImVec2 size = ImVec2(0, 0))
|
||||||
{
|
{
|
||||||
vec_t trans;
|
vec_t trans;
|
||||||
trans.TransformPoint(worldPos, mat);
|
trans.TransformPoint(worldPos, mat);
|
||||||
|
@ -821,7 +832,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
return ImVec2(trans.x, trans.y);
|
return ImVec2(trans.x, trans.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight))
|
static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, ImVec2 position = ImVec2(0, 0), ImVec2 size = ImVec2(0, 0))
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
@ -841,7 +852,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
rayEnd *= 1.f / rayEnd.w;
|
rayEnd *= 1.f / rayEnd.w;
|
||||||
rayDir = Normalized(rayEnd - rayOrigin);
|
rayDir = Normalized(rayEnd - rayOrigin);
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end, const bool localCoordinates = false)
|
static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end, const bool localCoordinates = false)
|
||||||
{
|
{
|
||||||
vec_t startOfSegment = start;
|
vec_t startOfSegment = start;
|
||||||
|
@ -911,7 +922,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
|
|
||||||
return vertPos1 + V * t;
|
return vertPos1 + V * t;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
static float IntersectRayPlane(const vec_t& rOrigin, const vec_t& rVector, const vec_t& plan)
|
static float IntersectRayPlane(const vec_t& rOrigin, const vec_t& rVector, const vec_t& plan)
|
||||||
{
|
{
|
||||||
const float numer = plan.Dot3(rOrigin) - plan.w;
|
const float numer = plan.Dot3(rOrigin) - plan.w;
|
||||||
|
@ -924,7 +935,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
|
|
||||||
return -(numer / denom);
|
return -(numer / denom);
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
static float DistanceToPlane(const vec_t& point, const vec_t& plan)
|
static float DistanceToPlane(const vec_t& point, const vec_t& plan)
|
||||||
{
|
{
|
||||||
return plan.Dot3(point) + plan.w;
|
return plan.Dot3(point) + plan.w;
|
||||||
|
@ -934,7 +945,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
{
|
{
|
||||||
return IsWithin(p.x, gContext.mX, gContext.mXMax) && IsWithin(p.y, gContext.mY, gContext.mYMax);
|
return IsWithin(p.x, gContext.mX, gContext.mXMax) && IsWithin(p.y, gContext.mY, gContext.mYMax);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
static bool IsHoveringWindow()
|
static bool IsHoveringWindow()
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *ImGui::GetCurrentContext();
|
ImGuiContext& g = *ImGui::GetCurrentContext();
|
||||||
|
@ -947,7 +958,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
void SetRect(float x, float y, float width, float height)
|
void SetRect(float x, float y, float width, float height)
|
||||||
{
|
{
|
||||||
gContext.mX = x;
|
gContext.mX = x;
|
||||||
|
@ -973,7 +984,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
{
|
{
|
||||||
ImGui::SetCurrentContext(ctx);
|
ImGui::SetCurrentContext(ctx);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
void BeginFrame()
|
void BeginFrame()
|
||||||
{
|
{
|
||||||
const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
|
const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
|
||||||
|
@ -997,7 +1008,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
ImGui::PopStyleColor(2);
|
ImGui::PopStyleColor(2);
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
bool IsUsing()
|
bool IsUsing()
|
||||||
{
|
{
|
||||||
return (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) || gContext.mbUsingBounds;
|
return (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) || gContext.mbUsingBounds;
|
||||||
|
@ -1045,14 +1056,16 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
gContext.mbUsingBounds = false;
|
gContext.mbUsingBounds = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
static void ComputeContext(const float* view, const float* projection, float* matrix, MODE mode)
|
static void ComputeContext(const float* view, const float* projection, float* matrix, MODE mode)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
gContext.mMode = mode;
|
gContext.mMode = mode;
|
||||||
|
#endif
|
||||||
gContext.mViewMat = *(matrix_t*)view;
|
gContext.mViewMat = *(matrix_t*)view;
|
||||||
gContext.mProjectionMat = *(matrix_t*)projection;
|
gContext.mProjectionMat = *(matrix_t*)projection;
|
||||||
gContext.mbMouseOver = IsHoveringWindow();
|
gContext.mbMouseOver = IsHoveringWindow();
|
||||||
|
#if 0
|
||||||
gContext.mModelLocal = *(matrix_t*)matrix;
|
gContext.mModelLocal = *(matrix_t*)matrix;
|
||||||
gContext.mModelLocal.OrthoNormalize();
|
gContext.mModelLocal.OrthoNormalize();
|
||||||
|
|
||||||
|
@ -1079,14 +1092,14 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
gContext.mCameraEye = viewInverse.v.position;
|
gContext.mCameraEye = viewInverse.v.position;
|
||||||
gContext.mCameraRight = viewInverse.v.right;
|
gContext.mCameraRight = viewInverse.v.right;
|
||||||
gContext.mCameraUp = viewInverse.v.up;
|
gContext.mCameraUp = viewInverse.v.up;
|
||||||
|
#endif
|
||||||
// projection reverse
|
// projection reverse
|
||||||
vec_t nearPos, farPos;
|
vec_t nearPos, farPos;
|
||||||
nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat);
|
nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat);
|
||||||
farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat);
|
farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat);
|
||||||
|
|
||||||
gContext.mReversed = (nearPos.z/nearPos.w) > (farPos.z / farPos.w);
|
gContext.mReversed = (nearPos.z/nearPos.w) > (farPos.z / farPos.w);
|
||||||
|
#if 0
|
||||||
// compute scale from the size of camera right vector projected on screen at the matrix position
|
// compute scale from the size of camera right vector projected on screen at the matrix position
|
||||||
vec_t pointRight = viewInverse.v.right;
|
vec_t pointRight = viewInverse.v.right;
|
||||||
pointRight.TransformPoint(gContext.mViewProjection);
|
pointRight.TransformPoint(gContext.mViewProjection);
|
||||||
|
@ -1101,10 +1114,10 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
gContext.mScreenSquareCenter = centerSSpace;
|
gContext.mScreenSquareCenter = centerSSpace;
|
||||||
gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f);
|
gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f);
|
||||||
gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f);
|
gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f);
|
||||||
|
#endif
|
||||||
ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector);
|
ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector);
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
static void ComputeColors(ImU32* colors, int type, OPERATION operation)
|
static void ComputeColors(ImU32* colors, int type, OPERATION operation)
|
||||||
{
|
{
|
||||||
if (gContext.mbEnable)
|
if (gContext.mbEnable)
|
||||||
|
@ -2775,8 +2788,8 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
bool ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor)
|
ViewManipulateResult ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor)
|
||||||
{
|
{
|
||||||
// Scale is always local or matrix will be skewed when applying world scale or oriented matrix
|
// Scale is always local or matrix will be skewed when applying world scale or oriented matrix
|
||||||
ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode);
|
ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode);
|
||||||
|
@ -2803,7 +2816,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
m16[15] = 1.0f;
|
m16[15] = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor)
|
ViewManipulateResult ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor)
|
||||||
{
|
{
|
||||||
static bool isDraging = false;
|
static bool isDraging = false;
|
||||||
static bool isClicking = false;
|
static bool isClicking = false;
|
||||||
|
@ -2933,6 +2946,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
overBox = boxCoordInt;
|
overBox = boxCoordInt;
|
||||||
isClicking = true;
|
isClicking = true;
|
||||||
isDraging = true;
|
isDraging = true;
|
||||||
|
interpolationFrames = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3039,7 +3053,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool viewUpdated = false;
|
ViewManipulateResult result;
|
||||||
if (interpolationFrames)
|
if (interpolationFrames)
|
||||||
{
|
{
|
||||||
interpolationFrames--;
|
interpolationFrames--;
|
||||||
|
@ -3053,7 +3067,7 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
newUp = interpolationUp;
|
newUp = interpolationUp;
|
||||||
vec_t newEye = camTarget + newDir * length;
|
vec_t newEye = camTarget + newDir * length;
|
||||||
LookAt(&newEye.x, &camTarget.x, &newUp.x, view);
|
LookAt(&newEye.x, &camTarget.x, &newUp.x, view);
|
||||||
viewUpdated = true;
|
result.changed = true;
|
||||||
}
|
}
|
||||||
isInside = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos);
|
isInside = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos);
|
||||||
|
|
||||||
|
@ -3081,68 +3095,70 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
|
|
||||||
if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f)
|
if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f)
|
||||||
{
|
{
|
||||||
vec_t right = viewInverse.v.right;
|
interpolationUp = overBox == 10 ? makeVect(1.f, 0.f, 0.f) : makeVect(-1.f, 0.f, 0.f);
|
||||||
if (fabsf(right.x) > fabsf(right.z))
|
|
||||||
{
|
|
||||||
right.z = 0.f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
right.x = 0.f;
|
|
||||||
}
|
|
||||||
right.Normalize();
|
|
||||||
interpolationUp = Cross(interpolationDir, right);
|
|
||||||
interpolationUp.Normalize();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
interpolationUp = referenceUp;
|
interpolationUp = referenceUp;
|
||||||
}
|
}
|
||||||
interpolationFrames = 40;
|
interpolationFrames = 40;
|
||||||
|
result.changed = true;
|
||||||
|
result.clicked_box = overBox;
|
||||||
}
|
}
|
||||||
isClicking = false;
|
isClicking = false;
|
||||||
isDraging = false;
|
isDraging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (isDraging)
|
if (isDraging && (fabsf(io.MouseDelta[0]) || fabsf(io.MouseDelta[1])))
|
||||||
{
|
{
|
||||||
matrix_t rx, ry, roll;
|
auto delta_x = io.MouseDelta.y * 0.01f;
|
||||||
|
auto delta_y = io.MouseDelta.x * 0.01f;
|
||||||
|
|
||||||
rx.RotationAxis(referenceUp, -io.MouseDelta.x * 0.01f);
|
matrix_t vvv = *(matrix_t*)view;
|
||||||
ry.RotationAxis(viewInverse.v.right, -io.MouseDelta.y * 0.01f);
|
// Calculate the rotation along x-axis
|
||||||
|
auto rot_x_deg = std::acos(std::clamp(Dot(vvv.v.up, referenceUp), -1.0f, 1.0f));
|
||||||
|
if (vvv.v.up.z < 0) rot_x_deg *= -1;
|
||||||
|
|
||||||
|
const vec_t referenceRight = makeVect(1.f, 0.f, 0.f);
|
||||||
|
const vec_t referenceForward = makeVect(0.f, 0.f, 1.f);
|
||||||
|
matrix_t rx2;
|
||||||
|
rx2.RotationAxis(referenceRight, rot_x_deg);
|
||||||
|
vec_t f2;
|
||||||
|
f2.TransformVector(referenceForward, rx2);
|
||||||
|
|
||||||
|
// Then calculate the rotation along y-axis
|
||||||
|
auto rot_y_deg = std::acos(std::clamp(Dot(vvv.v.dir, f2), -1.0f, 1.0f));
|
||||||
|
if (vvv.v.dir.x < 0) rot_y_deg *= -1;
|
||||||
|
|
||||||
|
// Apply deltas
|
||||||
|
rot_x_deg += delta_x;
|
||||||
|
rot_y_deg += delta_y;
|
||||||
|
// Clamp
|
||||||
|
if (rot_x_deg > 0.5 * M_PI) rot_x_deg = 0.5 * M_PI;
|
||||||
|
else if (rot_x_deg < -0.5 * M_PI) rot_x_deg = -0.5 * M_PI;
|
||||||
|
|
||||||
|
matrix_t rx, ry, roll;
|
||||||
|
// Calculate new rotation matrix
|
||||||
|
rx.RotationAxis(referenceRight, rot_x_deg);
|
||||||
|
f2.TransformVector(referenceUp, rx);
|
||||||
|
ry.RotationAxis(f2, rot_y_deg);
|
||||||
|
|
||||||
roll = rx * ry;
|
roll = rx * ry;
|
||||||
|
|
||||||
vec_t newDir = viewInverse.v.dir;
|
*(matrix_t*)view = roll;
|
||||||
newDir.TransformVector(roll);
|
|
||||||
newDir.Normalize();
|
|
||||||
|
|
||||||
// clamp
|
|
||||||
vec_t planDir = Cross(viewInverse.v.right, referenceUp);
|
|
||||||
planDir.y = 0.f;
|
|
||||||
planDir.Normalize();
|
|
||||||
float dt = Dot(planDir, newDir);
|
|
||||||
if (dt < 0.0f)
|
|
||||||
{
|
|
||||||
newDir += planDir * dt;
|
|
||||||
newDir.Normalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
vec_t newEye = camTarget + newDir * length;
|
|
||||||
LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view);
|
|
||||||
#if IMGUI_VERSION_NUM >= 18723
|
#if IMGUI_VERSION_NUM >= 18723
|
||||||
ImGui::SetNextFrameWantCaptureMouse(true);
|
ImGui::SetNextFrameWantCaptureMouse(true);
|
||||||
#else
|
#else
|
||||||
ImGui::CaptureMouseFromApp();
|
ImGui::CaptureMouseFromApp();
|
||||||
#endif
|
#endif
|
||||||
viewUpdated = true;
|
result.changed = true;
|
||||||
|
result.dragging = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore view/projection because it was used to compute ray
|
// restore view/projection because it was used to compute ray
|
||||||
ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode);
|
ComputeContext(svgView.m16, svgProjection.m16, nullptr/*gContext.mModelSource.m16*/, WORLD/*gContext.mMode*/);
|
||||||
|
|
||||||
return viewUpdated;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -206,15 +206,23 @@ namespace IMGUIZMO_NAMESPACE
|
||||||
};
|
};
|
||||||
|
|
||||||
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
|
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
|
||||||
|
|
||||||
|
struct ViewManipulateResult
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
bool dragging = false;
|
||||||
|
int clicked_box = -1;
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
|
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
|
||||||
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
|
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
|
||||||
// other software are using the same mechanics. But just in case, you are now warned!
|
// other software are using the same mechanics. But just in case, you are now warned!
|
||||||
//
|
//
|
||||||
IMGUI_API bool ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
IMGUI_API ViewManipulateResult ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
||||||
|
|
||||||
// use this version if you did not call Manipulate before and you are just using ViewManipulate
|
// use this version if you did not call Manipulate before and you are just using ViewManipulate
|
||||||
IMGUI_API bool ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
IMGUI_API ViewManipulateResult ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
||||||
|
|
||||||
IMGUI_API void SetID(int id);
|
IMGUI_API void SetID(int id);
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,9 @@ void AppConfig::set_defaults()
|
||||||
if (get("use_perspective_camera").empty())
|
if (get("use_perspective_camera").empty())
|
||||||
set_bool("use_perspective_camera", true);
|
set_bool("use_perspective_camera", true);
|
||||||
|
|
||||||
|
if (get("auto_perspective").empty())
|
||||||
|
set_bool("auto_perspective", false);
|
||||||
|
|
||||||
if (get("use_free_camera").empty())
|
if (get("use_free_camera").empty())
|
||||||
set_bool("use_free_camera", false);
|
set_bool("use_free_camera", false);
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ void Camera::set_type(EType type)
|
||||||
{
|
{
|
||||||
if (m_type != type && (type == EType::Ortho || type == EType::Perspective)) {
|
if (m_type != type && (type == EType::Ortho || type == EType::Perspective)) {
|
||||||
m_type = type;
|
m_type = type;
|
||||||
|
m_prevent_auto_type = true;
|
||||||
if (m_update_config_on_type_change_enabled) {
|
if (m_update_config_on_type_change_enabled) {
|
||||||
wxGetApp().app_config->set_bool("use_perspective_camera", m_type == EType::Perspective);
|
wxGetApp().app_config->set_bool("use_perspective_camera", m_type == EType::Perspective);
|
||||||
}
|
}
|
||||||
|
@ -52,6 +53,20 @@ void Camera::select_next_type()
|
||||||
set_type((EType)next);
|
set_type((EType)next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Camera::auto_type(EType preferred_type)
|
||||||
|
{
|
||||||
|
if (!wxGetApp().app_config->get_bool("auto_perspective")) return;
|
||||||
|
if (preferred_type == EType::Perspective) {
|
||||||
|
if (!m_prevent_auto_type) {
|
||||||
|
set_type(preferred_type);
|
||||||
|
m_prevent_auto_type = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set_type(preferred_type);
|
||||||
|
m_prevent_auto_type = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Camera::translate(const Vec3d& displacement) {
|
void Camera::translate(const Vec3d& displacement) {
|
||||||
if (!displacement.isApprox(Vec3d::Zero())) {
|
if (!displacement.isApprox(Vec3d::Zero())) {
|
||||||
m_view_matrix.translate(-displacement);
|
m_view_matrix.translate(-displacement);
|
||||||
|
@ -85,24 +100,41 @@ void Camera::set_zoom(double zoom)
|
||||||
|
|
||||||
void Camera::select_view(const std::string& direction)
|
void Camera::select_view(const std::string& direction)
|
||||||
{
|
{
|
||||||
if (direction == "iso")
|
if (direction == "iso") {
|
||||||
set_default_orientation();
|
set_default_orientation();
|
||||||
else if (direction == "left")
|
auto_type(EType::Perspective);
|
||||||
|
}
|
||||||
|
else if (direction == "left") {
|
||||||
look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
||||||
else if (direction == "right")
|
auto_type(EType::Ortho);
|
||||||
|
}
|
||||||
|
else if (direction == "right") {
|
||||||
look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
||||||
else if (direction == "top")
|
auto_type(EType::Ortho);
|
||||||
|
}
|
||||||
|
else if (direction == "top") {
|
||||||
look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY());
|
look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY());
|
||||||
else if (direction == "bottom")
|
auto_type(EType::Ortho);
|
||||||
|
}
|
||||||
|
else if (direction == "bottom") {
|
||||||
look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY());
|
look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY());
|
||||||
else if (direction == "front")
|
auto_type(EType::Ortho);
|
||||||
|
}
|
||||||
|
else if (direction == "front") {
|
||||||
look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
||||||
else if (direction == "rear")
|
auto_type(EType::Ortho);
|
||||||
|
}
|
||||||
|
else if (direction == "rear") {
|
||||||
look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
||||||
else if (direction == "topfront")
|
auto_type(EType::Ortho);
|
||||||
|
}
|
||||||
|
else if (direction == "topfront") {
|
||||||
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
||||||
|
auto_type(EType::Perspective);
|
||||||
|
}
|
||||||
else if (direction == "plate") {
|
else if (direction == "plate") {
|
||||||
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
||||||
|
auto_type(EType::Perspective);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct Camera
|
||||||
int requires_zoom_to_plate{ REQUIRES_ZOOM_TO_PLATE_IDLE };
|
int requires_zoom_to_plate{ REQUIRES_ZOOM_TO_PLATE_IDLE };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool m_prevent_auto_type = true;
|
||||||
EType m_type{ EType::Perspective };
|
EType m_type{ EType::Perspective };
|
||||||
bool m_update_config_on_type_change_enabled{ false };
|
bool m_update_config_on_type_change_enabled{ false };
|
||||||
Vec3d m_target{ Vec3d::Zero() };
|
Vec3d m_target{ Vec3d::Zero() };
|
||||||
|
@ -65,6 +66,7 @@ public:
|
||||||
// valid values for type: "false" -> ortho, "true" -> perspective
|
// valid values for type: "false" -> ortho, "true" -> perspective
|
||||||
void set_type(const std::string& type) { set_type((type == "true") ? EType::Perspective : EType::Ortho); }
|
void set_type(const std::string& type) { set_type((type == "true") ? EType::Perspective : EType::Ortho); }
|
||||||
void select_next_type();
|
void select_next_type();
|
||||||
|
void auto_type(EType preferred_type);
|
||||||
|
|
||||||
void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; }
|
void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; }
|
||||||
|
|
||||||
|
|
|
@ -3934,6 +3934,7 @@ void GLCanvas3D::on_gesture(wxGestureEvent &evt)
|
||||||
camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, plate->get_bounding_box().center());
|
camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, plate->get_bounding_box().center());
|
||||||
else
|
else
|
||||||
camera.rotate_on_sphere(-rotate, 0, rotate_limit);
|
camera.rotate_on_sphere(-rotate, 0, rotate_limit);
|
||||||
|
camera.auto_type(Camera::EType::Perspective);
|
||||||
}
|
}
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -4297,10 +4298,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
// Orca: Sphere rotation for painting view
|
// Orca: Sphere rotation for painting view
|
||||||
// if dragging over blank area with left button, rotate
|
// if dragging over blank area with left button, rotate
|
||||||
if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
|
if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
|
||||||
|
Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.);
|
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.);
|
||||||
if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports ||
|
if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports ||
|
||||||
m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) {
|
m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) {
|
||||||
Camera& camera = wxGetApp().plater()->get_camera();
|
|
||||||
Vec3d rotate_target = Vec3d::Zero();
|
Vec3d rotate_target = Vec3d::Zero();
|
||||||
if (!m_selection.is_empty())
|
if (!m_selection.is_empty())
|
||||||
rotate_target = m_selection.get_bounding_box().center();
|
rotate_target = m_selection.get_bounding_box().center();
|
||||||
|
@ -4311,14 +4312,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
else {
|
else {
|
||||||
if (wxGetApp().app_config->get_bool("use_free_camera"))
|
if (wxGetApp().app_config->get_bool("use_free_camera"))
|
||||||
// Virtual track ball (similar to the 3DConnexion mouse).
|
// Virtual track ball (similar to the 3DConnexion mouse).
|
||||||
wxGetApp().plater()->get_camera().rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.));
|
camera.rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.));
|
||||||
else {
|
else {
|
||||||
// Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation.
|
// Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation.
|
||||||
// It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(),
|
// It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(),
|
||||||
// which checks an atomics (flushes CPU caches).
|
// which checks an atomics (flushes CPU caches).
|
||||||
// See GH issue #3816.
|
// See GH issue #3816.
|
||||||
Camera& camera = wxGetApp().plater()->get_camera();
|
|
||||||
|
|
||||||
bool rotate_limit = current_printer_technology() != ptSLA;
|
bool rotate_limit = current_printer_technology() != ptSLA;
|
||||||
|
|
||||||
camera.recover_from_free_camera();
|
camera.recover_from_free_camera();
|
||||||
|
@ -4352,6 +4351,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
camera.auto_type(Camera::EType::Perspective);
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -5687,7 +5687,6 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo
|
||||||
return settings_changed;
|
return settings_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float identityMatrix[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
|
|
||||||
static const float cameraProjection[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
|
static const float cameraProjection[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
|
||||||
|
|
||||||
void GLCanvas3D::_render_3d_navigator()
|
void GLCanvas3D::_render_3d_navigator()
|
||||||
|
@ -5697,7 +5696,6 @@ void GLCanvas3D::_render_3d_navigator()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuizmo::BeginFrame();
|
ImGuizmo::BeginFrame();
|
||||||
ImGuizmo::AllowAxisFlip(false);
|
|
||||||
|
|
||||||
auto& style = ImGuizmo::GetStyle();
|
auto& style = ImGuizmo::GetStyle();
|
||||||
style.Colors[ImGuizmo::COLOR::DIRECTION_X] = ImGuiWrapper::to_ImVec4(ColorRGBA::Y());
|
style.Colors[ImGuizmo::COLOR::DIRECTION_X] = ImGuiWrapper::to_ImVec4(ColorRGBA::Y());
|
||||||
|
@ -5725,7 +5723,6 @@ void GLCanvas3D::_render_3d_navigator()
|
||||||
const float viewManipulateLeft = 0;
|
const float viewManipulateLeft = 0;
|
||||||
const float viewManipulateTop = io.DisplaySize.y;
|
const float viewManipulateTop = io.DisplaySize.y;
|
||||||
const float camDistance = 8.f;
|
const float camDistance = 8.f;
|
||||||
ImGuizmo::SetID(0);
|
|
||||||
|
|
||||||
Camera& camera = wxGetApp().plater()->get_camera();
|
Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
Transform3d m = Transform3d::Identity();
|
Transform3d m = Transform3d::Identity();
|
||||||
|
@ -5741,11 +5738,11 @@ void GLCanvas3D::_render_3d_navigator()
|
||||||
}
|
}
|
||||||
|
|
||||||
const float size = 128 * sc;
|
const float size = 128 * sc;
|
||||||
const bool dirty = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD,
|
const auto result = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD, nullptr,
|
||||||
identityMatrix, camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size),
|
camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size), ImVec2(size, size),
|
||||||
ImVec2(size, size), 0x00101010);
|
0x00101010);
|
||||||
|
|
||||||
if (dirty) {
|
if (result.changed) {
|
||||||
for (unsigned int c = 0; c < 4; ++c) {
|
for (unsigned int c = 0; c < 4; ++c) {
|
||||||
for (unsigned int r = 0; r < 4; ++r) {
|
for (unsigned int r = 0; r < 4; ++r) {
|
||||||
m(r, c) = cameraView[c * 4 + r];
|
m(r, c) = cameraView[c * 4 + r];
|
||||||
|
@ -5755,6 +5752,25 @@ void GLCanvas3D::_render_3d_navigator()
|
||||||
m = m * (coord_mapping_transform.inverse());
|
m = m * (coord_mapping_transform.inverse());
|
||||||
camera.set_rotation(m);
|
camera.set_rotation(m);
|
||||||
|
|
||||||
|
if (result.dragging) {
|
||||||
|
// Switch back to perspective view when normal dragging
|
||||||
|
camera.auto_type(Camera::EType::Perspective);
|
||||||
|
} else if (result.clicked_box >= 0) {
|
||||||
|
switch (result.clicked_box) {
|
||||||
|
case 4: // back
|
||||||
|
case 10: // top
|
||||||
|
case 12: // right
|
||||||
|
case 14: // left
|
||||||
|
case 16: // bottom
|
||||||
|
case 22: // front
|
||||||
|
// Automatically switch to orthographic view when click the center face of the navigator
|
||||||
|
camera.auto_type(Camera::EType::Ortho); break;
|
||||||
|
default:
|
||||||
|
// Otherwise switch back to perspective view
|
||||||
|
camera.auto_type(Camera::EType::Perspective); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
request_extra_frame();
|
request_extra_frame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2676,10 +2676,19 @@ void MainFrame::init_menubar_as_editor()
|
||||||
wxGetApp().app_config->set_bool("use_perspective_camera", false);
|
wxGetApp().app_config->set_bool("use_perspective_camera", false);
|
||||||
wxGetApp().update_ui_from_settings();
|
wxGetApp().update_ui_from_settings();
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
if (wxGetApp().app_config->get("use_perspective_camera").compare("true") == 0)
|
this->Bind(wxEVT_UPDATE_UI, [viewMenu, camera_id_base](wxUpdateUIEvent& evt) {
|
||||||
viewMenu->Check(wxID_CAMERA_PERSPECTIVE + camera_id_base, true);
|
if (wxGetApp().app_config->get("use_perspective_camera").compare("true") == 0)
|
||||||
else
|
viewMenu->Check(wxID_CAMERA_PERSPECTIVE + camera_id_base, true);
|
||||||
viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true);
|
else
|
||||||
|
viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true);
|
||||||
|
}, wxID_ANY);
|
||||||
|
append_menu_check_item(viewMenu, wxID_ANY, _L("Auto Perspective"), _L("Automatically switch between orthographic and perspective when changing from top/bottom/side views"),
|
||||||
|
[this](wxCommandEvent&) {
|
||||||
|
wxGetApp().app_config->set_bool("auto_perspective", !wxGetApp().app_config->get_bool("auto_perspective"));
|
||||||
|
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
|
||||||
|
},
|
||||||
|
this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; },
|
||||||
|
[this]() { return wxGetApp().app_config->get_bool("auto_perspective"); }, this);
|
||||||
|
|
||||||
viewMenu->AppendSeparator();
|
viewMenu->AppendSeparator();
|
||||||
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &G-code Window") + "\tC", _L("Show g-code window in Preview scene"),
|
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &G-code Window") + "\tC", _L("Show g-code window in Preview scene"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue