Skip to content

Commit

Permalink
Add a new Dial widget based on the knob of the Slider widget
Browse files Browse the repository at this point in the history
  • Loading branch information
ermarch committed May 17, 2019
1 parent aeebc45 commit 47d914c
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ add_library(nanogui-obj OBJECT
include/nanogui/combobox.h src/combobox.cpp
include/nanogui/progressbar.h src/progressbar.cpp
include/nanogui/slider.h src/slider.cpp
include/nanogui/dial.h src/dial.cpp
include/nanogui/messagedialog.h src/messagedialog.cpp
include/nanogui/textbox.h src/textbox.cpp
include/nanogui/imagepanel.h src/imagepanel.cpp
Expand Down
56 changes: 56 additions & 0 deletions include/nanogui/dial.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
nanogui/dial.h -- Fractional dial widget with mouse control
NanoGUI was developed by Wenzel Jakob <[email protected]>.
The widget drawing code is based on the NanoVG demo application
by Mikko Mononen.
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
/** \file */

#pragma once

#include <nanogui/widget.h>

NAMESPACE_BEGIN(nanogui)

/**
* \class Dial dial.h nanogui/dial.h
*
* \brief Fractional dial widget with mouse control.
*/
class NANOGUI_EXPORT Dial : public Widget {
public:
Dial(Widget *parent);

float value() const { return mValue; }
void setValue(float value) { mValue = value; }

std::pair<float, float> range() const { return mRange; }
void setRange(std::pair<float, float> range) { mRange = range; }

std::function<void(float)> callback() const { return mCallback; }
void setCallback(const std::function<void(float)> &callback) { mCallback = callback; }

std::function<void(float)> finalCallback() const { return mFinalCallback; }
void setFinalCallback(const std::function<void(float)> &callback) { mFinalCallback = callback; }

virtual Vector2i preferredSize(NVGcontext *ctx) const override;
virtual bool mouseDragEvent(const Vector2i &p, const Vector2i &rel, int button, int modifiers) override;
virtual bool mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) override;
virtual void draw(NVGcontext* ctx) override;
virtual void save(Serializer &s) const override;
virtual bool load(Serializer &s) override;

protected:
float mValue;
std::function<void(float)> mCallback;
std::function<void(float)> mFinalCallback;
std::pair<float, float> mRange;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};

NAMESPACE_END(nanogui)
1 change: 1 addition & 0 deletions include/nanogui/nanogui.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <nanogui/messagedialog.h>
#include <nanogui/textbox.h>
#include <nanogui/slider.h>
#include <nanogui/dial.h>
#include <nanogui/imagepanel.h>
#include <nanogui/imageview.h>
#include <nanogui/vscrollpanel.h>
Expand Down
130 changes: 130 additions & 0 deletions src/dial.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
nanogui/dial.cpp -- Fractional dial widget with mouse control
NanoGUI was developed by Wenzel Jakob <[email protected]>.
The widget drawing code is based on the NanoVG demo application
by Mikko Mononen.
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/

#include <nanogui/dial.h>
#include <nanogui/theme.h>
#include <nanogui/opengl.h>
#include <nanogui/serializer/core.h>
#include <Eigen/Geometry>

using Eigen::Rotation2Df;

NAMESPACE_BEGIN(nanogui)

Dial::Dial(Widget *parent)
: Widget(parent), mValue(0.0f), mRange(0.f, 1.f) {
}

Vector2i Dial::preferredSize(NVGcontext *) const {
return Vector2i(40, 40);
}

bool Dial::mouseDragEvent(const Vector2i &p, const Vector2i & /* rel */,
int /* button */, int /* modifiers */) {
if (!mEnabled)
return false;

Vector2f pos = (p - mPos - mSize/2).cast<float>();
float value = 0.5f + 0.5f*atan2f(pos.x(), -pos.y())/NVG_PI;
value = -0.1f + 1.2f*value;

value = value * (mRange.second - mRange.first) + mRange.first;
mValue = std::min(std::max(value, mRange.first), mRange.second);
if (mCallback)
mCallback(mValue);

return true;
}

bool Dial::mouseButtonEvent(const Vector2i &p, int /* button */, bool down, int /* modifiers */) {
if (!mEnabled)
return false;

if (down) {
Vector2f pos = (p - mPos - mSize/2).cast<float>();
float kr = 0.5f * (mSize.y() * 0.4f);

if (pos.squaredNorm() >= kr*kr) {
float value = 0.5f + 0.5f*atan2f(pos.x(), -pos.y())/NVG_PI;
value = -0.1f + 1.2f*value;

value = value * (mRange.second - mRange.first) + mRange.first;
mValue = std::min(std::max(value, mRange.first), mRange.second);
}
if (mCallback)
mCallback(mValue);
} else if (mFinalCallback) {
mFinalCallback(mValue);
}

return true;
}

void Dial::draw(NVGcontext* ctx) {
Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
float kr = (int) (mSize.y() * 0.4f), kshadow = 2;

Vector2f dialPos(center.x(), center.y() + 0.5f);

NVGpaint dial = nvgLinearGradient(ctx,
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
mTheme->mBorderLight, mTheme->mBorderMedium);
NVGpaint dialReverse = nvgLinearGradient(ctx,
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
mTheme->mBorderMedium,
mTheme->mBorderLight);

NVGpaint dialFace = nvgRadialGradient(ctx,
dialPos.x(), dialPos.y(), kr - kshadow,
kr + kshadow, Color(150, mEnabled ? 255 : 100), mTheme->mTransparent);

nvgBeginPath(ctx);
nvgCircle(ctx, dialPos.x(), dialPos.y(), kr);
nvgStrokeColor(ctx, mTheme->mBorderDark);
nvgFillPaint(ctx, dial);
nvgStroke(ctx);
nvgFill(ctx);
nvgBeginPath(ctx);
nvgCircle(ctx, dialPos.x(), dialPos.y(), kr - kshadow);
nvgFillPaint(ctx, dialFace);
nvgStrokePaint(ctx, dialReverse);
nvgStroke(ctx);
nvgFill(ctx);

Vector2f notchPos(0.0f, 0.8f*(kr - 1.5f*kshadow));
float value = (mValue - mRange.first)/(mRange.second - mRange.first);
float theta = 2.0f*NVG_PI*(0.1f + 0.8f*value);
Rotation2Df t(theta);
notchPos = t*notchPos;
notchPos += dialPos;

nvgBeginPath(ctx);
nvgCircle(ctx, notchPos.x(), notchPos.y(), 0.15f*kr);
nvgFillColor(ctx, Color(mEnabled ? 50 : 100, 150));
nvgStrokePaint(ctx, dial);
nvgStroke(ctx);
nvgFill(ctx);
}

void Dial::save(Serializer &s) const {
Widget::save(s);
s.set("value", mValue);
s.set("range", mRange);
}

bool Dial::load(Serializer &s) {
if (!Widget::load(s)) return false;
if (!s.get("value", mValue)) return false;
if (!s.get("range", mRange)) return false;
return true;
}

NAMESPACE_END(nanogui)
29 changes: 29 additions & 0 deletions src/example1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <nanogui/layout.h>
#include <nanogui/label.h>
#include <nanogui/checkbox.h>
#include <nanogui/dial.h>
#include <nanogui/button.h>
#include <nanogui/toolbutton.h>
#include <nanogui/popupbutton.h>
Expand All @@ -35,6 +36,7 @@
#include <nanogui/graph.h>
#include <nanogui/tabwidget.h>
#include <iostream>
#include <sstream>
#include <string>

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

new Label(window, "Dial and text box", "sans-bold");

panel = new Widget(window);
panel->setLayout(new BoxLayout(Orientation::Horizontal,
Alignment::Middle, 0, 20));

Dial *dial = new Dial(panel);
dial->setValue(0.01f);
dial->setFixedWidth(80);

textBox = new TextBox(panel);
textBox->setFixedSize(Vector2i(60, 25));
textBox->setValue("0.01");
dial->setCallback([textBox](float value) {
value = 0.01f + 99.99f*powf(value, 5.0f);
std::ostringstream sval;
sval.precision(2); sval << std::fixed << value;
textBox->setValue(sval.str());
});
dial->setFinalCallback([&](float value) {
value = 0.01f + 99.99f*powf(value, 5.0f);
cout << "Final dial value: " << value << endl;
});
textBox->setFixedSize(Vector2i(60,25));
textBox->setFontSize(20);
textBox->setAlignment(TextBox::Alignment::Right);

window = new Window(this, "Misc. widgets");
window->setPosition(Vector2i(425,15));
window->setLayout(new GroupLayout());
Expand Down

0 comments on commit 47d914c

Please sign in to comment.