Skip to content

Commit 47d914c

Browse files
committed
Add a new Dial widget based on the knob of the Slider widget
1 parent aeebc45 commit 47d914c

5 files changed

Lines changed: 217 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ add_library(nanogui-obj OBJECT
330330
include/nanogui/combobox.h src/combobox.cpp
331331
include/nanogui/progressbar.h src/progressbar.cpp
332332
include/nanogui/slider.h src/slider.cpp
333+
include/nanogui/dial.h src/dial.cpp
333334
include/nanogui/messagedialog.h src/messagedialog.cpp
334335
include/nanogui/textbox.h src/textbox.cpp
335336
include/nanogui/imagepanel.h src/imagepanel.cpp

include/nanogui/dial.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
nanogui/dial.h -- Fractional dial widget with mouse control
3+
4+
NanoGUI was developed by Wenzel Jakob <[email protected]>.
5+
The widget drawing code is based on the NanoVG demo application
6+
by Mikko Mononen.
7+
8+
All rights reserved. Use of this source code is governed by a
9+
BSD-style license that can be found in the LICENSE.txt file.
10+
*/
11+
/** \file */
12+
13+
#pragma once
14+
15+
#include <nanogui/widget.h>
16+
17+
NAMESPACE_BEGIN(nanogui)
18+
19+
/**
20+
* \class Dial dial.h nanogui/dial.h
21+
*
22+
* \brief Fractional dial widget with mouse control.
23+
*/
24+
class NANOGUI_EXPORT Dial : public Widget {
25+
public:
26+
Dial(Widget *parent);
27+
28+
float value() const { return mValue; }
29+
void setValue(float value) { mValue = value; }
30+
31+
std::pair<float, float> range() const { return mRange; }
32+
void setRange(std::pair<float, float> range) { mRange = range; }
33+
34+
std::function<void(float)> callback() const { return mCallback; }
35+
void setCallback(const std::function<void(float)> &callback) { mCallback = callback; }
36+
37+
std::function<void(float)> finalCallback() const { return mFinalCallback; }
38+
void setFinalCallback(const std::function<void(float)> &callback) { mFinalCallback = callback; }
39+
40+
virtual Vector2i preferredSize(NVGcontext *ctx) const override;
41+
virtual bool mouseDragEvent(const Vector2i &p, const Vector2i &rel, int button, int modifiers) override;
42+
virtual bool mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) override;
43+
virtual void draw(NVGcontext* ctx) override;
44+
virtual void save(Serializer &s) const override;
45+
virtual bool load(Serializer &s) override;
46+
47+
protected:
48+
float mValue;
49+
std::function<void(float)> mCallback;
50+
std::function<void(float)> mFinalCallback;
51+
std::pair<float, float> mRange;
52+
public:
53+
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
54+
};
55+
56+
NAMESPACE_END(nanogui)

include/nanogui/nanogui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <nanogui/messagedialog.h>
3030
#include <nanogui/textbox.h>
3131
#include <nanogui/slider.h>
32+
#include <nanogui/dial.h>
3233
#include <nanogui/imagepanel.h>
3334
#include <nanogui/imageview.h>
3435
#include <nanogui/vscrollpanel.h>

src/dial.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
nanogui/dial.cpp -- Fractional dial widget with mouse control
3+
4+
NanoGUI was developed by Wenzel Jakob <[email protected]>.
5+
The widget drawing code is based on the NanoVG demo application
6+
by Mikko Mononen.
7+
8+
All rights reserved. Use of this source code is governed by a
9+
BSD-style license that can be found in the LICENSE.txt file.
10+
*/
11+
12+
#include <nanogui/dial.h>
13+
#include <nanogui/theme.h>
14+
#include <nanogui/opengl.h>
15+
#include <nanogui/serializer/core.h>
16+
#include <Eigen/Geometry>
17+
18+
using Eigen::Rotation2Df;
19+
20+
NAMESPACE_BEGIN(nanogui)
21+
22+
Dial::Dial(Widget *parent)
23+
: Widget(parent), mValue(0.0f), mRange(0.f, 1.f) {
24+
}
25+
26+
Vector2i Dial::preferredSize(NVGcontext *) const {
27+
return Vector2i(40, 40);
28+
}
29+
30+
bool Dial::mouseDragEvent(const Vector2i &p, const Vector2i & /* rel */,
31+
int /* button */, int /* modifiers */) {
32+
if (!mEnabled)
33+
return false;
34+
35+
Vector2f pos = (p - mPos - mSize/2).cast<float>();
36+
float value = 0.5f + 0.5f*atan2f(pos.x(), -pos.y())/NVG_PI;
37+
value = -0.1f + 1.2f*value;
38+
39+
value = value * (mRange.second - mRange.first) + mRange.first;
40+
mValue = std::min(std::max(value, mRange.first), mRange.second);
41+
if (mCallback)
42+
mCallback(mValue);
43+
44+
return true;
45+
}
46+
47+
bool Dial::mouseButtonEvent(const Vector2i &p, int /* button */, bool down, int /* modifiers */) {
48+
if (!mEnabled)
49+
return false;
50+
51+
if (down) {
52+
Vector2f pos = (p - mPos - mSize/2).cast<float>();
53+
float kr = 0.5f * (mSize.y() * 0.4f);
54+
55+
if (pos.squaredNorm() >= kr*kr) {
56+
float value = 0.5f + 0.5f*atan2f(pos.x(), -pos.y())/NVG_PI;
57+
value = -0.1f + 1.2f*value;
58+
59+
value = value * (mRange.second - mRange.first) + mRange.first;
60+
mValue = std::min(std::max(value, mRange.first), mRange.second);
61+
}
62+
if (mCallback)
63+
mCallback(mValue);
64+
} else if (mFinalCallback) {
65+
mFinalCallback(mValue);
66+
}
67+
68+
return true;
69+
}
70+
71+
void Dial::draw(NVGcontext* ctx) {
72+
Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
73+
float kr = (int) (mSize.y() * 0.4f), kshadow = 2;
74+
75+
Vector2f dialPos(center.x(), center.y() + 0.5f);
76+
77+
NVGpaint dial = nvgLinearGradient(ctx,
78+
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
79+
mTheme->mBorderLight, mTheme->mBorderMedium);
80+
NVGpaint dialReverse = nvgLinearGradient(ctx,
81+
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
82+
mTheme->mBorderMedium,
83+
mTheme->mBorderLight);
84+
85+
NVGpaint dialFace = nvgRadialGradient(ctx,
86+
dialPos.x(), dialPos.y(), kr - kshadow,
87+
kr + kshadow, Color(150, mEnabled ? 255 : 100), mTheme->mTransparent);
88+
89+
nvgBeginPath(ctx);
90+
nvgCircle(ctx, dialPos.x(), dialPos.y(), kr);
91+
nvgStrokeColor(ctx, mTheme->mBorderDark);
92+
nvgFillPaint(ctx, dial);
93+
nvgStroke(ctx);
94+
nvgFill(ctx);
95+
nvgBeginPath(ctx);
96+
nvgCircle(ctx, dialPos.x(), dialPos.y(), kr - kshadow);
97+
nvgFillPaint(ctx, dialFace);
98+
nvgStrokePaint(ctx, dialReverse);
99+
nvgStroke(ctx);
100+
nvgFill(ctx);
101+
102+
Vector2f notchPos(0.0f, 0.8f*(kr - 1.5f*kshadow));
103+
float value = (mValue - mRange.first)/(mRange.second - mRange.first);
104+
float theta = 2.0f*NVG_PI*(0.1f + 0.8f*value);
105+
Rotation2Df t(theta);
106+
notchPos = t*notchPos;
107+
notchPos += dialPos;
108+
109+
nvgBeginPath(ctx);
110+
nvgCircle(ctx, notchPos.x(), notchPos.y(), 0.15f*kr);
111+
nvgFillColor(ctx, Color(mEnabled ? 50 : 100, 150));
112+
nvgStrokePaint(ctx, dial);
113+
nvgStroke(ctx);
114+
nvgFill(ctx);
115+
}
116+
117+
void Dial::save(Serializer &s) const {
118+
Widget::save(s);
119+
s.set("value", mValue);
120+
s.set("range", mRange);
121+
}
122+
123+
bool Dial::load(Serializer &s) {
124+
if (!Widget::load(s)) return false;
125+
if (!s.get("value", mValue)) return false;
126+
if (!s.get("range", mRange)) return false;
127+
return true;
128+
}
129+
130+
NAMESPACE_END(nanogui)

src/example1.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <nanogui/layout.h>
1919
#include <nanogui/label.h>
2020
#include <nanogui/checkbox.h>
21+
#include <nanogui/dial.h>
2122
#include <nanogui/button.h>
2223
#include <nanogui/toolbutton.h>
2324
#include <nanogui/popupbutton.h>
@@ -35,6 +36,7 @@
3536
#include <nanogui/graph.h>
3637
#include <nanogui/tabwidget.h>
3738
#include <iostream>
39+
#include <sstream>
3840
#include <string>
3941

4042
// Includes for the GLTexture class.
@@ -334,6 +336,33 @@ class ExampleApplication : public nanogui::Screen {
334336
textBox->setFontSize(20);
335337
textBox->setAlignment(TextBox::Alignment::Right);
336338

339+
new Label(window, "Dial and text box", "sans-bold");
340+
341+
panel = new Widget(window);
342+
panel->setLayout(new BoxLayout(Orientation::Horizontal,
343+
Alignment::Middle, 0, 20));
344+
345+
Dial *dial = new Dial(panel);
346+
dial->setValue(0.01f);
347+
dial->setFixedWidth(80);
348+
349+
textBox = new TextBox(panel);
350+
textBox->setFixedSize(Vector2i(60, 25));
351+
textBox->setValue("0.01");
352+
dial->setCallback([textBox](float value) {
353+
value = 0.01f + 99.99f*powf(value, 5.0f);
354+
std::ostringstream sval;
355+
sval.precision(2); sval << std::fixed << value;
356+
textBox->setValue(sval.str());
357+
});
358+
dial->setFinalCallback([&](float value) {
359+
value = 0.01f + 99.99f*powf(value, 5.0f);
360+
cout << "Final dial value: " << value << endl;
361+
});
362+
textBox->setFixedSize(Vector2i(60,25));
363+
textBox->setFontSize(20);
364+
textBox->setAlignment(TextBox::Alignment::Right);
365+
337366
window = new Window(this, "Misc. widgets");
338367
window->setPosition(Vector2i(425,15));
339368
window->setLayout(new GroupLayout());

0 commit comments

Comments
 (0)