From a23a68d683ae4bb2462474888a8ba537e4afd1af Mon Sep 17 00:00:00 2001 From: CGarcia Date: Sun, 20 Nov 2022 18:18:25 +0100 Subject: [PATCH 1/8] Added rotation behaviour. Added two new flags ImGuiKnobFlags_RotateRelative and ImGuiKnobFlags_RotateAbsolute. With them you have to move around the knob in a circular way. Keyboard input is not currently implemented for these modes. --- imgui-knobs.cpp | 143 +++++++++++++++++++++++++++++++++++++++++++++++- imgui-knobs.h | 2 + 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/imgui-knobs.cpp b/imgui-knobs.cpp index 9f59a5c..ca952ba 100644 --- a/imgui-knobs.cpp +++ b/imgui-knobs.cpp @@ -9,6 +9,46 @@ namespace ImGuiKnobs { namespace detail { + + + + //Re-maps a number from one range to another. + //Numbers outside the range are not clamped to 0 and 1. + template + inline T Map(const T& aValue,const T& aIStart,const T& aIStop,const T& aOStart,const T& aOStop) + { + return aOStart + (aOStop - aOStart) * ((aValue - aIStart) / (aIStop - aIStart)); + } + + + // Re-Maps clamped + template + inline T MapC(const T& aValue,const T& aIStart,const T& aIStop,const T& aOStart,const T& aOStop) + { + if(aValue<=aIStart) + { + return aOStart; + } + else if(aValue>=aIStop) + { + return aOStop; + } + return Map(aValue,aIStart,aIStop,aOStart,aOStop); + } + + inline float GetAngle(const ImVec2& aPos,const ImVec2& aMousePos) + { + ImVec2 dir(aMousePos.x-aPos.x,aMousePos.y-aPos.y); + float angle=atan2(-dir.y,-dir.x); + angle+=(3.141592f*2.5f); + angle=fmod(angle,3.141592f*2.0f); + + return angle; + } + + + + void draw_arc1(ImVec2 center, float radius, float start_angle, float end_angle, float thickness, ImColor color, int num_segments) { ImVec2 start = { center[0] + cosf(start_angle) * radius, @@ -66,23 +106,120 @@ namespace ImGuiKnobs { float angle_cos; float angle_sin; + + bool WrapAround(DataType p_value,DataType prev_value, DataType v_min, DataType v_max) const noexcept + { + static const float epsilon=0.05f; + + if( (p_value>=v_max*(1.0f-epsilon) && prev_value<=v_min*(1.0f+epsilon)) + || (p_value<=v_min*(1.0f+epsilon) && prev_value>=v_max*(1.0f-epsilon)) + ) + { + return true; + } + + return false; + } + + bool RotateBehavior(DataType *p_value, DataType v_min, DataType v_max,float speed,bool absolute_rot) + { + ImGuiIO& imgui_io=ImGui::GetIO(); + + + if(absolute_rot) + { + ImVec2 mouse_pos=imgui_io.MousePos; + float input_angle=GetAngle(center,mouse_pos); + DataType prev_value=*p_value; + + *p_value=MapC(input_angle,0.25f*3.141592f,1.75f*3.141592f,1.0f*v_min,1.0f*v_max); + + if( WrapAround(*p_value,prev_value,v_min,v_max) || fabs(*p_value-prev_value)>(v_min+v_max)*0.75f) + { + *p_value=prev_value; + } + + value_changed=(prev_value!=*p_value); + } + else + { + ImVec2 mouse_pos,mouse_pos_prev; + + if(imgui_io.MouseDownDuration[0]>0.0f) + { + mouse_pos=imgui_io.MousePos; + + mouse_pos_prev=imgui_io.MousePos; + mouse_pos_prev.x-=imgui_io.MouseDelta.x; + mouse_pos_prev.y-=imgui_io.MouseDelta.y; + } + else + { + mouse_pos=imgui_io.MouseClickedPos[0]; + mouse_pos_prev=imgui_io.MouseClickedPos[0]; + } + + float input_angle_prev=GetAngle(center,mouse_pos_prev); + float input_angle=GetAngle(center,mouse_pos); + + if(input_angle_prev!=input_angle) + { + DataType prev_value=*p_value; + //if(!speed) // speed == 0 is changed in knob_with_drag + { + DataType r=(v_max-v_min)*IMGUIKNOBS_PI*0.4f; + speed=r; + } + + *p_value+=MapC(input_angle-input_angle_prev,-2.0f*IMGUIKNOBS_PI,2.0f*IMGUIKNOBS_PI,-speed,speed); + if (*p_value < v_min) + *p_value = v_min; + if (*p_value > v_max) + *p_value = v_max; + + if(WrapAround(*p_value,prev_value,v_min,v_max) || fabs(input_angle-input_angle_prev)>IMGUIKNOBS_PI) + { + *p_value=prev_value; + value_changed=false; + } + else + { + value_changed=true; + } + } + else + { + value_changed=false; + } + } + + return value_changed; + } + + knob(const char *_label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, float _radius, const char *format, ImGuiKnobFlags flags) { radius = _radius; t = ((float) *p_value - v_min) / (v_max - v_min); auto screen_pos = ImGui::GetCursorScreenPos(); + center = {screen_pos[0] + radius, screen_pos[1] + radius}; // Handle dragging ImGui::InvisibleButton(_label, {radius * 2.0f, radius * 2.0f}); auto gid = ImGui::GetID(_label); ImGuiSliderFlags drag_flags = 0; - if (!(flags & ImGuiKnobFlags_DragHorizontal)) { + if((flags & (ImGuiKnobFlags_RotateRelative|ImGuiKnobFlags_RotateAbsolute))) { + value_changed = RotateBehavior(p_value, v_min, v_max,speed,flags==ImGuiKnobFlags_RotateAbsolute); + } + else{ + if (!(flags & ImGuiKnobFlags_DragHorizontal)) drag_flags |= ImGuiSliderFlags_Vertical; + + value_changed = ImGui::DragBehavior(gid, data_type, p_value, speed, &v_min, &v_max, format, drag_flags); } - value_changed = ImGui::DragBehavior(gid, data_type, p_value, speed, &v_min, &v_max, format, drag_flags); + angle_min = IMGUIKNOBS_PI * 0.75f; angle_max = IMGUIKNOBS_PI * 2.25f; - center = {screen_pos[0] + radius, screen_pos[1] + radius}; is_active = ImGui::IsItemActive(); is_hovered = ImGui::IsItemHovered(); angle = angle_min + (angle_max - angle_min) * t; diff --git a/imgui-knobs.h b/imgui-knobs.h index 9587634..afaacb9 100644 --- a/imgui-knobs.h +++ b/imgui-knobs.h @@ -10,6 +10,8 @@ enum ImGuiKnobFlags_ { ImGuiKnobFlags_NoInput = 1 << 1, ImGuiKnobFlags_ValueTooltip = 1 << 2, ImGuiKnobFlags_DragHorizontal = 1 << 3, + ImGuiKnobFlags_RotateRelative = 1 << 4, + ImGuiKnobFlags_RotateAbsolute = 1 << 5, }; typedef int ImGuiKnobVariant; From 664eac4ac3e65c7b3796841ddc63a6156e27b4aa Mon Sep 17 00:00:00 2001 From: CGarcia Date: Sat, 26 Nov 2022 18:46:47 +0100 Subject: [PATCH 2/8] Code style adapted to be in line with the rest of the code. --- imgui-knobs.cpp | 176 ++++++++++++++++++++---------------------------- 1 file changed, 72 insertions(+), 104 deletions(-) diff --git a/imgui-knobs.cpp b/imgui-knobs.cpp index ca952ba..36e3084 100644 --- a/imgui-knobs.cpp +++ b/imgui-knobs.cpp @@ -11,43 +11,26 @@ namespace ImGuiKnobs { namespace detail { - - //Re-maps a number from one range to another. - //Numbers outside the range are not clamped to 0 and 1. - template - inline T Map(const T& aValue,const T& aIStart,const T& aIStop,const T& aOStart,const T& aOStop) - { - return aOStart + (aOStop - aOStart) * ((aValue - aIStart) / (aIStop - aIStart)); - } - - - // Re-Maps clamped - template - inline T MapC(const T& aValue,const T& aIStart,const T& aIStop,const T& aOStart,const T& aOStop) - { - if(aValue<=aIStart) - { - return aOStart; - } - else if(aValue>=aIStop) - { - return aOStop; + // Re-Maps clamped + template + inline T map_range_clamped(const T& value, const T& in_start, const T& in_stop, const T& out_start, const T& out_stop) { + if (value <= in_start) { + return out_start; + } + else if (value >= in_stop) { + return out_stop; + } + return out_start + (out_stop - out_start) * ((value - in_start) / (in_stop - in_start)); } - return Map(aValue,aIStart,aIStop,aOStart,aOStop); - } - - inline float GetAngle(const ImVec2& aPos,const ImVec2& aMousePos) - { - ImVec2 dir(aMousePos.x-aPos.x,aMousePos.y-aPos.y); - float angle=atan2(-dir.y,-dir.x); - angle+=(3.141592f*2.5f); - angle=fmod(angle,3.141592f*2.0f); - - return angle; - } - + inline float get_angle(const ImVec2& point1, const ImVec2& point2) { + ImVec2 dir(point2.x - point1.x, point2.y - point1.y); + float angle = atan2(-dir.y, -dir.x); + angle += (IMGUIKNOBS_PI * 2.5f); + angle = fmod(angle, IMGUIKNOBS_PI * 2.0f); + return angle; + } void draw_arc1(ImVec2 center, float radius, float start_angle, float end_angle, float thickness, ImColor color, int num_segments) { ImVec2 start = { @@ -107,93 +90,78 @@ namespace ImGuiKnobs { float angle_sin; - bool WrapAround(DataType p_value,DataType prev_value, DataType v_min, DataType v_max) const noexcept - { - static const float epsilon=0.05f; + bool wrap_around(DataType p_value,DataType prev_value, DataType v_min, DataType v_max) const noexcept { + static const float epsilon = 0.05f; - if( (p_value>=v_max*(1.0f-epsilon) && prev_value<=v_min*(1.0f+epsilon)) - || (p_value<=v_min*(1.0f+epsilon) && prev_value>=v_max*(1.0f-epsilon)) - ) - { - return true; + if ( (p_value >= v_max * (1.0f - epsilon) && prev_value <= v_min * (1.0f + epsilon)) + || (p_value <= v_min * (1.0f + epsilon) && prev_value >= v_max * (1.0f - epsilon))) { + return true; } - return false; + return false; } - bool RotateBehavior(DataType *p_value, DataType v_min, DataType v_max,float speed,bool absolute_rot) - { - ImGuiIO& imgui_io=ImGui::GetIO(); - + bool rotate_behavior(DataType *p_value, DataType v_min, DataType v_max,float speed,bool absolute_rot) { + ImGuiIO& imgui_io=ImGui::GetIO(); - if(absolute_rot) - { - ImVec2 mouse_pos=imgui_io.MousePos; - float input_angle=GetAngle(center,mouse_pos); - DataType prev_value=*p_value; + if (absolute_rot) { + ImVec2 mouse_pos = imgui_io.MousePos; + float input_angle = get_angle(center ,mouse_pos); + DataType prev_value = *p_value; - *p_value=MapC(input_angle,0.25f*3.141592f,1.75f*3.141592f,1.0f*v_min,1.0f*v_max); + *p_value = static_cast(map_range_clamped(input_angle, 0.25f * IMGUIKNOBS_PI, 1.75f * IMGUIKNOBS_PI, 1.0f * v_min, 1.0f * v_max)); - if( WrapAround(*p_value,prev_value,v_min,v_max) || fabs(*p_value-prev_value)>(v_min+v_max)*0.75f) - { - *p_value=prev_value; + if( wrap_around(*p_value, prev_value, v_min, v_max) || fabs(*p_value - prev_value) > (v_min + v_max) * 0.75f) { + *p_value = prev_value; } - value_changed=(prev_value!=*p_value); + value_changed = (prev_value != *p_value); } - else - { - ImVec2 mouse_pos,mouse_pos_prev; + else { + ImVec2 mouse_pos, mouse_pos_prev; - if(imgui_io.MouseDownDuration[0]>0.0f) - { - mouse_pos=imgui_io.MousePos; + if (imgui_io.MouseDownDuration[0] > 0.0f) { + mouse_pos = imgui_io.MousePos; - mouse_pos_prev=imgui_io.MousePos; - mouse_pos_prev.x-=imgui_io.MouseDelta.x; - mouse_pos_prev.y-=imgui_io.MouseDelta.y; + mouse_pos_prev = imgui_io.MousePos; + mouse_pos_prev.x -= imgui_io.MouseDelta.x; + mouse_pos_prev.y -= imgui_io.MouseDelta.y; } - else - { - mouse_pos=imgui_io.MouseClickedPos[0]; - mouse_pos_prev=imgui_io.MouseClickedPos[0]; + else { + mouse_pos = imgui_io.MouseClickedPos[0]; + mouse_pos_prev = imgui_io.MouseClickedPos[0]; } - float input_angle_prev=GetAngle(center,mouse_pos_prev); - float input_angle=GetAngle(center,mouse_pos); + float input_angle_prev = get_angle(center,mouse_pos_prev); + float input_angle = get_angle(center,mouse_pos); - if(input_angle_prev!=input_angle) - { - DataType prev_value=*p_value; - //if(!speed) // speed == 0 is changed in knob_with_drag + if (input_angle_prev != input_angle) { + DataType prev_value = *p_value; + //if (!speed) // speed == 0 is changed in knob_with_drag. { - DataType r=(v_max-v_min)*IMGUIKNOBS_PI*0.4f; - speed=r; + speed = (v_max-v_min) * IMGUIKNOBS_PI * 0.4f; } - *p_value+=MapC(input_angle-input_angle_prev,-2.0f*IMGUIKNOBS_PI,2.0f*IMGUIKNOBS_PI,-speed,speed); - if (*p_value < v_min) - *p_value = v_min; - if (*p_value > v_max) - *p_value = v_max; - - if(WrapAround(*p_value,prev_value,v_min,v_max) || fabs(input_angle-input_angle_prev)>IMGUIKNOBS_PI) - { - *p_value=prev_value; - value_changed=false; - } - else - { - value_changed=true; - } + *p_value += static_cast(map_range_clamped(input_angle - input_angle_prev, -2.0f * IMGUIKNOBS_PI, 2.0f * IMGUIKNOBS_PI, -speed, speed)); + if (*p_value < v_min) + *p_value = v_min; + if (*p_value > v_max) + *p_value = v_max; + + if (wrap_around(*p_value, prev_value, v_min, v_max) || fabs(input_angle - input_angle_prev) > IMGUIKNOBS_PI) { + *p_value = prev_value; + value_changed = false; + } + else { + value_changed = true; + } } - else - { - value_changed=false; + else { + value_changed = false; } } - return value_changed; + return value_changed; } @@ -207,14 +175,14 @@ namespace ImGuiKnobs { ImGui::InvisibleButton(_label, {radius * 2.0f, radius * 2.0f}); auto gid = ImGui::GetID(_label); ImGuiSliderFlags drag_flags = 0; - if((flags & (ImGuiKnobFlags_RotateRelative|ImGuiKnobFlags_RotateAbsolute))) { - value_changed = RotateBehavior(p_value, v_min, v_max,speed,flags==ImGuiKnobFlags_RotateAbsolute); - } - else{ - if (!(flags & ImGuiKnobFlags_DragHorizontal)) - drag_flags |= ImGuiSliderFlags_Vertical; + if((flags & (ImGuiKnobFlags_RotateRelative | ImGuiKnobFlags_RotateAbsolute))) { + value_changed = rotate_behavior(p_value, v_min, v_max, speed, flags == ImGuiKnobFlags_RotateAbsolute); + } + else { + if (!(flags & ImGuiKnobFlags_DragHorizontal)) + drag_flags |= ImGuiSliderFlags_Vertical; - value_changed = ImGui::DragBehavior(gid, data_type, p_value, speed, &v_min, &v_max, format, drag_flags); + value_changed = ImGui::DragBehavior(gid, data_type, p_value, speed, &v_min, &v_max, format, drag_flags); } From fdb7051d9aa75382e2989dbdd571db6e7a6ef49e Mon Sep 17 00:00:00 2001 From: CGarcia Date: Mon, 6 Mar 2023 19:46:39 +0100 Subject: [PATCH 3/8] Improved drag to not work when the click occurs outside the widget. --- imgui-knobs.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/imgui-knobs.cpp b/imgui-knobs.cpp index 36e3084..c2d23fb 100644 --- a/imgui-knobs.cpp +++ b/imgui-knobs.cpp @@ -101,9 +101,20 @@ namespace ImGuiKnobs { return false; } - bool rotate_behavior(DataType *p_value, DataType v_min, DataType v_max,float speed,bool absolute_rot) { + bool rotate_behavior(ImGuiID id,DataType *p_value, DataType v_min, DataType v_max,float speed,bool absolute_rot) { ImGuiIO& imgui_io=ImGui::GetIO(); + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) { + if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) + ImGui::ClearActiveID(); + else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + ImGui::ClearActiveID(); + } + + if (g.ActiveId != id) + return false; + if (absolute_rot) { ImVec2 mouse_pos = imgui_io.MousePos; float input_angle = get_angle(center ,mouse_pos); @@ -174,11 +185,12 @@ namespace ImGuiKnobs { // Handle dragging ImGui::InvisibleButton(_label, {radius * 2.0f, radius * 2.0f}); auto gid = ImGui::GetID(_label); - ImGuiSliderFlags drag_flags = 0; + if((flags & (ImGuiKnobFlags_RotateRelative | ImGuiKnobFlags_RotateAbsolute))) { - value_changed = rotate_behavior(p_value, v_min, v_max, speed, flags == ImGuiKnobFlags_RotateAbsolute); + value_changed = rotate_behavior(gid,p_value, v_min, v_max, speed, flags == ImGuiKnobFlags_RotateAbsolute); } else { + ImGuiSliderFlags drag_flags = 0; if (!(flags & ImGuiKnobFlags_DragHorizontal)) drag_flags |= ImGuiSliderFlags_Vertical; From 43d06b6332d7f489b7f8c27b93afd8634c3fce45 Mon Sep 17 00:00:00 2001 From: CGarcia Date: Mon, 24 Apr 2023 21:28:47 +0200 Subject: [PATCH 4/8] =?UTF-8?q?Ajustes=20por=20actualizaci=C3=B3n=20de=20i?= =?UTF-8?q?mgui.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- imgui-knobs.cpp | 5 +++-- imgui-knobs.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui-knobs.cpp b/imgui-knobs.cpp index c2d23fb..f18c153 100644 --- a/imgui-knobs.cpp +++ b/imgui-knobs.cpp @@ -2,6 +2,7 @@ #include #include +#define IMGUI_DEFINE_MATH_OPERATORS #include #include @@ -56,7 +57,7 @@ namespace ImGuiKnobs { auto *draw_list = ImGui::GetWindowDrawList(); - draw_list->AddBezierCurve(start, arc1, arc2, end, color, thickness, num_segments); + draw_list->AddBezierCubic(start, arc1, arc2, end, color, thickness, num_segments); } void draw_arc(ImVec2 center, float radius, float start_angle, float end_angle, float thickness, ImColor color, int num_segments, int bezier_count) { @@ -108,7 +109,7 @@ namespace ImGuiKnobs { if (g.ActiveId == id) { if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ImGui::ClearActiveID(); - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ImGui::ClearActiveID(); } diff --git a/imgui-knobs.h b/imgui-knobs.h index afaacb9..65bca51 100644 --- a/imgui-knobs.h +++ b/imgui-knobs.h @@ -1,6 +1,8 @@ #pragma once #include + +#define IMGUI_DEFINE_MATH_OPERATORS #include typedef int ImGuiKnobFlags; From f537e22a616200375132f4d71cf62c002380b456 Mon Sep 17 00:00:00 2001 From: CGarcia Date: Sun, 22 Oct 2023 18:23:27 +0200 Subject: [PATCH 5/8] Now it's possible to specify the min and max draw angles. --- imgui-knobs.cpp | 23 ++++++++++++++--------- imgui-knobs.h | 3 ++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/imgui-knobs.cpp b/imgui-knobs.cpp index f18c153..7c4e73c 100644 --- a/imgui-knobs.cpp +++ b/imgui-knobs.cpp @@ -177,7 +177,8 @@ namespace ImGuiKnobs { } - knob(const char *_label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, float _radius, const char *format, ImGuiKnobFlags flags) { + knob(const char *_label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, float _radius, const char *format, ImGuiKnobFlags flags + ,float aAngleMin=IMGUIKNOBS_PI*0.75f,float aAngleMax=IMGUIKNOBS_PI*2.25f) { radius = _radius; t = ((float) *p_value - v_min) / (v_max - v_min); auto screen_pos = ImGui::GetCursorScreenPos(); @@ -199,8 +200,8 @@ namespace ImGuiKnobs { } - angle_min = IMGUIKNOBS_PI * 0.75f; - angle_max = IMGUIKNOBS_PI * 2.25f; + angle_min = aAngleMin; + angle_max = aAngleMax; is_active = ImGui::IsItemActive(); is_hovered = ImGui::IsItemHovered(); angle = angle_min + (angle_max - angle_min) * t; @@ -258,7 +259,8 @@ namespace ImGuiKnobs { }; template - knob knob_with_drag(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float _speed, const char *format, float size, ImGuiKnobFlags flags) { + knob knob_with_drag(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float _speed, const char *format, float size, ImGuiKnobFlags flags + ,float aAngleMin=IMGUIKNOBS_PI*0.75f,float aAngleMax=IMGUIKNOBS_PI*2.25f) { auto speed = _speed == 0 ? (v_max - v_min) / 250.f : _speed; ImGui::PushID(label); auto width = size == 0 ? ImGui::GetTextLineHeight() * 4.0f : size * ImGui::GetIO().FontGlobalScale; @@ -281,7 +283,8 @@ namespace ImGuiKnobs { } // Draw knob - knob k(label, data_type, p_value, v_min, v_max, speed, width * 0.5f, format, flags); + knob k(label, data_type, p_value, v_min, v_max, speed, width * 0.5f, format, flags + ,aAngleMin,aAngleMax); // Draw tooltip if (flags & ImGuiKnobFlags_ValueTooltip && (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) || ImGui::IsItemActive())) { @@ -341,8 +344,9 @@ namespace ImGuiKnobs { template - bool BaseKnob(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps = 10) { - auto knob = detail::knob_with_drag(label, data_type, p_value, v_min, v_max, speed, format, size, flags); + bool BaseKnob(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps = 10 + ,float aAngleMin=IMGUIKNOBS_PI*0.75f,float aAngleMax=IMGUIKNOBS_PI*2.25f) { + auto knob = detail::knob_with_drag(label, data_type, p_value, v_min, v_max, speed, format, size, flags,aAngleMin,aAngleMax); switch (variant) { case ImGuiKnobVariant_Tick: { @@ -405,9 +409,10 @@ namespace ImGuiKnobs { return knob.value_changed; } - bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps) { + bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps + ,float aAngleMin,float aAngleMax) { const char *_format = format == NULL ? "%.3f" : format; - return BaseKnob(label, ImGuiDataType_Float, p_value, v_min, v_max, speed, _format, variant, size, flags, steps); + return BaseKnob(label,ImGuiDataType_Float,p_value,v_min,v_max,speed,_format,variant,size,flags,steps,aAngleMin,aAngleMax); } bool KnobInt(const char *label, int *p_value, int v_min, int v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps) { diff --git a/imgui-knobs.h b/imgui-knobs.h index 65bca51..6217aac 100644 --- a/imgui-knobs.h +++ b/imgui-knobs.h @@ -44,6 +44,7 @@ namespace ImGuiKnobs { } }; - bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed = 0, const char *format = NULL, ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0, ImGuiKnobFlags flags = 0, int steps = 10); + bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed = 0, const char *format = NULL, ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0, ImGuiKnobFlags flags = 0, int steps = 10 + ,float aAngleMin=3.14159265358979323846f*0.75f,float aAngleMax=3.14159265358979323846f*2.25f); bool KnobInt(const char *label, int *p_value, int v_min, int v_max, float speed = 0, const char *format = NULL, ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0, ImGuiKnobFlags flags = 0, int steps = 10); }// namespace ImGuiKnobs From 36beff239e4c110510c545999ed847058151701c Mon Sep 17 00:00:00 2001 From: CGarcia Date: Sat, 4 Nov 2023 23:11:46 +0100 Subject: [PATCH 6/8] Fixed flag check for absolute behaviour --- imgui-knobs.cpp | 2 +- imgui-knobs.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui-knobs.cpp b/imgui-knobs.cpp index 7c4e73c..0734275 100644 --- a/imgui-knobs.cpp +++ b/imgui-knobs.cpp @@ -189,7 +189,7 @@ namespace ImGuiKnobs { auto gid = ImGui::GetID(_label); if((flags & (ImGuiKnobFlags_RotateRelative | ImGuiKnobFlags_RotateAbsolute))) { - value_changed = rotate_behavior(gid,p_value, v_min, v_max, speed, flags == ImGuiKnobFlags_RotateAbsolute); + value_changed = rotate_behavior(gid,p_value, v_min, v_max, speed, flags & ImGuiKnobFlags_RotateAbsolute); } else { ImGuiSliderFlags drag_flags = 0; diff --git a/imgui-knobs.h b/imgui-knobs.h index 6217aac..5a5bcdf 100644 --- a/imgui-knobs.h +++ b/imgui-knobs.h @@ -14,6 +14,7 @@ enum ImGuiKnobFlags_ { ImGuiKnobFlags_DragHorizontal = 1 << 3, ImGuiKnobFlags_RotateRelative = 1 << 4, ImGuiKnobFlags_RotateAbsolute = 1 << 5, + ImGuiKnobFlags_WrapAround = 1 << 6, }; typedef int ImGuiKnobVariant; From 05d256a8c9ad32c1ef167bb1c97f4c95a0ff0881 Mon Sep 17 00:00:00 2001 From: CGarcia Date: Sat, 4 Nov 2023 23:14:23 +0100 Subject: [PATCH 7/8] Added wrap around mode for rotate behaviours. Also, modified code is written with my preferred style as it seems that upstream is not interested in merging. --- imgui-knobs.cpp | 132 +++++++++++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 46 deletions(-) diff --git a/imgui-knobs.cpp b/imgui-knobs.cpp index 0734275..c257ea9 100644 --- a/imgui-knobs.cpp +++ b/imgui-knobs.cpp @@ -8,6 +8,8 @@ #define IMGUIKNOBS_PI 3.14159265358979323846f + + namespace ImGuiKnobs { namespace detail { @@ -77,7 +79,8 @@ namespace ImGuiKnobs { } template - struct knob { + struct knob + { float radius; bool value_changed; ImVec2 center; @@ -91,86 +94,119 @@ namespace ImGuiKnobs { float angle_sin; - bool wrap_around(DataType p_value,DataType prev_value, DataType v_min, DataType v_max) const noexcept { - static const float epsilon = 0.05f; + bool wrap_around(DataType p_value,DataType prev_value, DataType v_min, DataType v_max) const noexcept + { + static const float kEpsilon = 0.05f; - if ( (p_value >= v_max * (1.0f - epsilon) && prev_value <= v_min * (1.0f + epsilon)) - || (p_value <= v_min * (1.0f + epsilon) && prev_value >= v_max * (1.0f - epsilon))) { + if ( (p_value >= v_max * (1.0f - kEpsilon) && prev_value <= v_min * (1.0f + kEpsilon)) + || (p_value <= v_min * (1.0f + kEpsilon) && prev_value >= v_max * (1.0f - kEpsilon))) + { return true; - } + } return false; } - bool rotate_behavior(ImGuiID id,DataType *p_value, DataType v_min, DataType v_max,float speed,bool absolute_rot) { + bool rotate_behavior(ImGuiID id,DataType *p_value, DataType v_min, DataType v_max,float speed + ,bool absolute_rot,bool aWrapAround) + { ImGuiIO& imgui_io=ImGui::GetIO(); ImGuiContext& g = *GImGui; - if (g.ActiveId == id) { + if (g.ActiveId == id) + { if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ImGui::ClearActiveID(); else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ImGui::ClearActiveID(); - } + } if (g.ActiveId != id) return false; - if (absolute_rot) { + if (absolute_rot) + { ImVec2 mouse_pos = imgui_io.MousePos; float input_angle = get_angle(center ,mouse_pos); DataType prev_value = *p_value; - *p_value = static_cast(map_range_clamped(input_angle, 0.25f * IMGUIKNOBS_PI, 1.75f * IMGUIKNOBS_PI, 1.0f * v_min, 1.0f * v_max)); - - if( wrap_around(*p_value, prev_value, v_min, v_max) || fabs(*p_value - prev_value) > (v_min + v_max) * 0.75f) { - *p_value = prev_value; - } + *p_value = static_cast(map_range_clamped(input_angle,angle_min,angle_max, 1.0f * v_min, 1.0f * v_max)); + if(!aWrapAround) + { + if( wrap_around(*p_value, prev_value, v_min, v_max) || fabs(*p_value - prev_value) > (v_min + v_max) * 0.75f) + { + *p_value = prev_value; + } + } value_changed = (prev_value != *p_value); - } - else { + } + else + { ImVec2 mouse_pos, mouse_pos_prev; - if (imgui_io.MouseDownDuration[0] > 0.0f) { + if(imgui_io.MouseDownDuration[0] > 0.0f) + { mouse_pos = imgui_io.MousePos; mouse_pos_prev = imgui_io.MousePos; mouse_pos_prev.x -= imgui_io.MouseDelta.x; mouse_pos_prev.y -= imgui_io.MouseDelta.y; - } - else { + } + else + { mouse_pos = imgui_io.MouseClickedPos[0]; mouse_pos_prev = imgui_io.MouseClickedPos[0]; - } + } float input_angle_prev = get_angle(center,mouse_pos_prev); float input_angle = get_angle(center,mouse_pos); - if (input_angle_prev != input_angle) { + if (input_angle_prev != input_angle) + { DataType prev_value = *p_value; //if (!speed) // speed == 0 is changed in knob_with_drag. { speed = (v_max-v_min) * IMGUIKNOBS_PI * 0.4f; } - *p_value += static_cast(map_range_clamped(input_angle - input_angle_prev, -2.0f * IMGUIKNOBS_PI, 2.0f * IMGUIKNOBS_PI, -speed, speed)); - if (*p_value < v_min) - *p_value = v_min; - if (*p_value > v_max) - *p_value = v_max; - - if (wrap_around(*p_value, prev_value, v_min, v_max) || fabs(input_angle - input_angle_prev) > IMGUIKNOBS_PI) { - *p_value = prev_value; - value_changed = false; - } - else { + if(!aWrapAround) + { + *p_value += static_cast(map_range_clamped(input_angle - input_angle_prev, -2.0f * IMGUIKNOBS_PI, 2.0f * IMGUIKNOBS_PI, -speed, speed)); + if (*p_value < v_min) + *p_value = v_min; + if (*p_value > v_max) + *p_value = v_max; + + if (wrap_around(*p_value, prev_value, v_min, v_max) || fabs(input_angle - input_angle_prev) > IMGUIKNOBS_PI) + { + *p_value = prev_value; + value_changed = false; + } + else + { + value_changed = true; + } + } + else + { + *p_value += static_cast(input_angle-input_angle_prev); + if(*p_value < v_min) + { + *p_value = v_max-(v_min-*p_value); + } + if(*p_value > v_max) + { + *p_value = v_min+(*p_value-v_max); + } + value_changed = true; - } - } - else { + } + } + else + { value_changed = false; - } + } } return value_changed; @@ -178,8 +214,11 @@ namespace ImGuiKnobs { knob(const char *_label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, float _radius, const char *format, ImGuiKnobFlags flags - ,float aAngleMin=IMGUIKNOBS_PI*0.75f,float aAngleMax=IMGUIKNOBS_PI*2.25f) { + ,float aAngleMin=IMGUIKNOBS_PI*0.75f,float aAngleMax=IMGUIKNOBS_PI*2.25f) + { radius = _radius; + angle_min = aAngleMin; + angle_max = aAngleMax; t = ((float) *p_value - v_min) / (v_max - v_min); auto screen_pos = ImGui::GetCursorScreenPos(); center = {screen_pos[0] + radius, screen_pos[1] + radius}; @@ -188,20 +227,21 @@ namespace ImGuiKnobs { ImGui::InvisibleButton(_label, {radius * 2.0f, radius * 2.0f}); auto gid = ImGui::GetID(_label); - if((flags & (ImGuiKnobFlags_RotateRelative | ImGuiKnobFlags_RotateAbsolute))) { - value_changed = rotate_behavior(gid,p_value, v_min, v_max, speed, flags & ImGuiKnobFlags_RotateAbsolute); - } - else { + if((flags & (ImGuiKnobFlags_RotateRelative | ImGuiKnobFlags_RotateAbsolute))) + { + value_changed = rotate_behavior(gid,p_value, v_min, v_max, speed + , flags & ImGuiKnobFlags_RotateAbsolute + , flags & ImGuiKnobFlags_WrapAround); + } + else + { ImGuiSliderFlags drag_flags = 0; if (!(flags & ImGuiKnobFlags_DragHorizontal)) drag_flags |= ImGuiSliderFlags_Vertical; value_changed = ImGui::DragBehavior(gid, data_type, p_value, speed, &v_min, &v_max, format, drag_flags); - } + } - - angle_min = aAngleMin; - angle_max = aAngleMax; is_active = ImGui::IsItemActive(); is_hovered = ImGui::IsItemHovered(); angle = angle_min + (angle_max - angle_min) * t; From f1a598407b60fe5b59de8fdb7095ccde121804f0 Mon Sep 17 00:00:00 2001 From: CGarcia Date: Mon, 6 Nov 2023 18:24:24 +0100 Subject: [PATCH 8/8] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 713b2d7..9819224 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ImGui Knobs -This is a port/adaptation of [imgui-rs-knobs](https://github.com/DGriffin91/imgui-rs-knobs), for C++. +I have made some changes to [imgui-knobs](https://github.com/altschuler/imgui-knobs), which is a port/adaptation of [imgui-rs-knobs](https://github.com/DGriffin91/imgui-rs-knobs), for C++. ![image](https://user-images.githubusercontent.com/956928/164050142-96a8dde4-7d2e-43e4-9afe-14ab48eac243.png) @@ -18,7 +18,7 @@ if (ImGuiKnobs::Knob("Volume", &value, -6.0f, 6.0f, 0.1f, "%.1fdB", ImGuiKnobVar Draw knobs using either `Knob` or `KnobInt`. The API is: ``` -bool ImGuiKnobs::Knob(label, *value, min, max, [speed, format, variant, size, flags, steps]) +bool ImGuiKnobs::Knob(label, *value, min, max, [speed, format, variant, size, flags, steps, angle_min, angle_max]) bool ImGuiKnobs::KnobInt(label, *value, min, max, [speed, format, variant, size, flags, steps]) ``` @@ -30,6 +30,9 @@ bool ImGuiKnobs::KnobInt(label, *value, min, max, [speed, format, variant, size, - `ImGuiKnobFlags_NoInput`: Hide the bottom drag input. - `ImGuiKnobFlags_ValueTooltip`: Show a tooltip with the current value on hover. - `ImGuiKnobFlags_DragHorizontal`: Use horizontal dragging (default is vertical). + - `ImGuiKnobFlags_RotateRelative`: Use rotate relative dragging. + - `ImGuiKnobFlags_RotateAbsolute`: Use rotate absolute dragging. + - `ImGuiKnobFlags_WrapAround`: Values wraparound when using the "rotate" flags. ### Size You can specify a size given as the width of the knob (will be scaled according to ImGui's `FontGlobalScale`). Default (0) will use 4x line height.