Skip to content
Snippets Groups Projects
Commit f998e98a authored by Sebastian Hahta's avatar Sebastian Hahta
Browse files

combobox to inherit from popupwindow

parent 698e1639
No related branches found
No related tags found
1 merge request!316Resolves #343 GUI and Frame Refactor
......@@ -22,10 +22,10 @@ set(GUI2SRC
src/screen.cpp
src/view.cpp
src/gltexture.cpp
src/widgets/frameview.cpp
src/widgets/soundctrl.cpp
src/widgets/popupbutton.cpp
src/widgets/imageview.cpp
src/widgets/combobox.cpp
)
add_gui_module("themes")
......
#include "addsource.hpp"
#include "../modules/addsource.hpp"
#include "../widgets/combobox.hpp"
#include <nanogui/layout.h>
#include <nanogui/button.h>
#include <nanogui/combobox.h>
using ftl::gui2::AddSourceWindow;
......@@ -16,7 +18,7 @@ AddSourceWindow::AddSourceWindow(nanogui::Widget* parent, AddCtrl *ctrl) :
setLayout(new GroupLayout(15, 6, 14, 10));
setPosition(Vector2i(parent->width()/2.0f - 100.0f, parent->height()/2.0f - 100.0f));
auto *type_select = new ComboBox(this, {
auto *type_select = new ftl::gui2::ComboBox(this, {
"Stereo Camera", "Pylon Camera", "Screen Capture", "Virtual Camera", "FTL File", "Network Stream"
});
......
......@@ -5,7 +5,6 @@
#include "../gltexture.hpp"
#include "../widgets/window.hpp"
#include "../widgets/frameview.hpp"
#include "../widgets/soundctrl.hpp"
#include "../widgets/imageview.hpp"
......
#pragma once
#include "../widgets/window.hpp"
#include "../widgets/frameview.hpp"
#include "../view.hpp"
......
/*
src/combobox.cpp -- simple combo box widget based on a popup button
NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
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 "combobox.hpp"
#include <nanogui/layout.h>
#include <nanogui/serializer/core.h>
#include <cassert>
using nanogui::Vector2i;
using nanogui::Vector2f;
using nanogui::GroupLayout;
using nanogui::Serializer;
using ftl::gui2::ComboBox;
using ftl::gui2::PopupButton;
ComboBox::ComboBox(Widget *parent) : PopupButton(parent), mSelectedIndex(0) {
}
ComboBox::ComboBox(Widget *parent, const std::vector<std::string> &items)
: PopupButton(parent), mSelectedIndex(0) {
setItems(items);
}
ComboBox::ComboBox(Widget *parent, const std::vector<std::string> &items, const std::vector<std::string> &itemsShort)
: PopupButton(parent), mSelectedIndex(0) {
setItems(items, itemsShort);
}
void ComboBox::setSelectedIndex(int idx) {
if (mItemsShort.empty())
return;
const std::vector<Widget *> &children = popup()->children();
((Button *) children[mSelectedIndex])->setPushed(false);
((Button *) children[idx])->setPushed(true);
mSelectedIndex = idx;
setCaption(mItemsShort[idx]);
}
void ComboBox::setItems(const std::vector<std::string> &items, const std::vector<std::string> &itemsShort) {
assert(items.size() == itemsShort.size());
mItems = items;
mItemsShort = itemsShort;
if (mSelectedIndex < 0 || mSelectedIndex >= (int) items.size())
mSelectedIndex = 0;
while (mPopup->childCount() != 0)
mPopup->removeChild(mPopup->childCount()-1);
mPopup->setLayout(new GroupLayout(10));
int index = 0;
for (const auto &str: items) {
Button *button = new Button(mPopup, str);
button->setFlags(Button::RadioButton);
button->setCallback([&, index] {
mSelectedIndex = index;
setCaption(mItemsShort[index]);
setPushed(false);
popup()->setVisible(false);
if (mCallback)
mCallback(index);
});
index++;
}
setSelectedIndex(mSelectedIndex);
}
bool ComboBox::scrollEvent(const Vector2i &p, const Vector2f &rel) {
if (rel.y() < 0) {
setSelectedIndex(std::min(mSelectedIndex+1, (int)(items().size()-1)));
if (mCallback)
mCallback(mSelectedIndex);
return true;
} else if (rel.y() > 0) {
setSelectedIndex(std::max(mSelectedIndex-1, 0));
if (mCallback)
mCallback(mSelectedIndex);
return true;
}
return Widget::scrollEvent(p, rel);
}
void ComboBox::save(Serializer &s) const {
Widget::save(s);
s.set("items", mItems);
s.set("itemsShort", mItemsShort);
s.set("selectedIndex", mSelectedIndex);
}
bool ComboBox::load(Serializer &s) {
if (!Widget::load(s)) return false;
if (!s.get("items", mItems)) return false;
if (!s.get("itemsShort", mItemsShort)) return false;
if (!s.get("selectedIndex", mSelectedIndex)) return false;
return true;
}
/*
Modification: Inherits from ftl::gui2::PopupButton
NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
The nanogui::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 nanogui/combobox.h
*
* \brief Simple combo box nanogui::Widget based on a popup button.
*/
#pragma once
#include "popupbutton.hpp"
namespace ftl {
namespace gui2 {
/**
* \class ComboBox combobox.h nanogui/combobox.h
*
* \brief Simple combo box nanogui::Widget based on a popup button.
*/
class NANOGUI_EXPORT ComboBox : public PopupButton {
public:
/// Create an empty combo box
ComboBox(nanogui::Widget *parent);
/// Create a new combo box with the given items
ComboBox(nanogui::Widget *parent, const std::vector<std::string> &items);
/**
* \brief Create a new combo box with the given items, providing both short and
* long descriptive labels for each item
*/
ComboBox(nanogui::Widget *parent, const std::vector<std::string> &items,
const std::vector<std::string> &itemsShort);
/// The callback to execute for this ComboBox.
std::function<void(int)> callback() const { return mCallback; }
/// Sets the callback to execute for this ComboBox.
void setCallback(const std::function<void(int)> &callback) { mCallback = callback; }
/// The current index this ComboBox has selected.
int selectedIndex() const { return mSelectedIndex; }
/// Sets the current index this ComboBox has selected.
void setSelectedIndex(int idx);
/// Sets the items for this ComboBox, providing both short and long descriptive lables for each item.
void setItems(const std::vector<std::string> &items, const std::vector<std::string> &itemsShort);
/// Sets the items for this ComboBox.
void setItems(const std::vector<std::string> &items) { setItems(items, items); }
/// The items associated with this ComboBox.
const std::vector<std::string> &items() const { return mItems; }
/// The short descriptions associated with this ComboBox.
const std::vector<std::string> &itemsShort() const { return mItemsShort; }
/// Handles mouse scrolling events for this ComboBox.
virtual bool scrollEvent(const nanogui::Vector2i &p, const nanogui::Vector2f &rel) override;
/// Saves the state of this ComboBox to the specified nanogui::Serializer.
virtual void save(nanogui::Serializer &s) const override;
/// Sets the state of this ComboBox from the specified nanogui::Serializer.
virtual bool load(nanogui::Serializer &s) override;
protected:
/// The items associated with this ComboBox.
std::vector<std::string> mItems;
/// The short descriptions of items associated with this ComboBox.
std::vector<std::string> mItemsShort;
/// The callback for this ComboBox.
std::function<void(int)> mCallback;
/// The current index this ComboBox has selected.
int mSelectedIndex;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
}
}
#include <loguru.hpp>
#include <nanogui/screen.h>
#include <ftl/cuda_common.hpp>
#include <ftl/cuda_texture.hpp>
#include <ftl/exception.hpp>
#include "gltexture.hpp"
#include "frameview.hpp"
using ftl::gui2::FrameView;
using ftl::codecs::Channel;
// ==== OpenGL shaders =========================================================
namespace {
// modify gl_Position to flip y axis (flip sign)
const std::string GLFrameViewVertexShader =
R"(#version 330
uniform vec2 scaleFactor;
uniform vec2 position;
in vec2 vertex;
out vec2 uv;
void main() {
uv = vertex;
vec2 scaledVertex = (vertex * scaleFactor) + position;
gl_Position = vec4(2.0*scaledVertex.x - 1.0,
1.0 - 2.0*scaledVertex.y,
0.0, 1.0);
})";
// ==== Fragment Shaders (display/colorize/...) ============================
// https://github.com/kbinani/colormap-shaders/
// TODO: for more visualizations include the whole library
const std::string GLColormapJet =
R"(
float colormap_red(float x) {
if (x < 0.7) {
return 4.0 * x - 1.5;
} else {
return -4.0 * x + 4.5;
}
}
float colormap_green(float x) {
if (x < 0.5) {
return 4.0 * x - 0.5;
} else {
return -4.0 * x + 3.5;
}
}
float colormap_blue(float x) {
if (x < 0.3) {
return 4.0 * x + 0.5;
} else {
return -4.0 * x + 2.5;
}
}
vec4 colormap(float x) {
float r = clamp(colormap_red(x), 0.0, 1.0);
float g = clamp(colormap_green(x), 0.0, 1.0);
float b = clamp(colormap_blue(x), 0.0, 1.0);
return vec4(r, g, b, 1.0);
}
)";
// normalize floating point value (depth) to [0.0, 1.0] range
const std::string GLFloatNormalize =
R"(
float normalizef(float v, float min, float max) {
return clamp((v - min)/(max - min), 0.0, 1.0);
}
)";
// set color.w = 1.0f ; possibly bug: incorrect value from getTexture()?
const std::string GLFrameViewFragmentShader =
R"(#version 330
uniform sampler2D image;
out vec4 color;
in vec2 uv;
void main() {
color = texture(image, uv);
color.w = 1.0f;
}
)";
const std::string GLFrameViewFragmentShaderColorize =
R"(#version 330
)"
+ GLFloatNormalize
+ GLColormapJet
+
R"(
uniform sampler2D image;
// normalization can be done here if it is possible to read unclamped
// values in fragment shader
//uniform float vmin = 0.0;
//uniform float vmax = 1.0;
out vec4 color;
in vec2 uv;
void main() {
//float v = texelFetch(image, ivec2(textureSize(image ,0) * uv).xy, 0).x;
//color = colormap(normalizef(v, vmin, vmax));
color = colormap(texture(image, uv).x);
}
)";
const std::string GLFrameViewFragmentShaderBlend =
R"(#version 330
uniform sampler2D image1;
uniform sampler2D image2;
uniform sampler2D depthImage;
uniform float blendAmount;
out vec4 color;
in vec2 uv;
void main() {
// TODO: use same colorization as above? also requires gl_FragDepth
color = blendAmount * texture(image1, uv) + (1.0 - blendAmount) * texture(image2, uv);
color.w = 1.0f;
gl_FragDepth = texture(depthImage, uv).r;
})";
}
void buildFrameViewShader(nanogui::GLShader &shader, std::string name, std::string fragmentShader) {
shader = nanogui::GLShader();
shader.init(name, GLFrameViewVertexShader, fragmentShader);
nanogui::MatrixXu indices(3, 2);
indices.col(0) << 0, 1, 2;
indices.col(1) << 2, 3, 1;
nanogui::MatrixXf vertices(2, 4);
vertices.col(0) << 0, 0;
vertices.col(1) << 1, 0;
vertices.col(2) << 0, 1;
vertices.col(3) << 1, 1;
shader.bind();
shader.uploadIndices(indices);
shader.uploadAttrib("vertex", vertices);
}
// =============================================================================
FrameView::FrameView(nanogui::Widget *parent) :
Widget(parent), texture(), fs_(nullptr) {
if (glfwGetCurrentContext() == nullptr) {
throw FTL_Error("No current OpenGL context");
}
buildShader(Channel::Colour);
}
void FrameView::reset() {
std::atomic_store(&fs_, {});
if (texture.isValid()) {
texture.free();
}
}
void FrameView::draw(NVGcontext *ctx) {
Widget::draw(ctx);
auto* fs = std::atomic_load(&fs_).get();
if (fs) {
if (fs->hasFrame(fid_)) {
set(ctx, fs->frames[fid_].cast<ftl::rgbd::Frame>(), channel_, stream_, copy_);
}
std::atomic_store(&fs_, {});
}
if (flush_) {
// Flush the NanoVG draw stack, not necessary to call
// nvgBeginFrame afterwards.
nvgEndFrame(ctx);
}
if (!texture.isValid()) {
return;
}
const nanogui::Vector2f imageSize = nanogui::Vector2f(texture.width(), texture.height());
const nanogui::Vector2f widgetSize = size().cast<float>();
const nanogui::Vector2f screenSize = screen()->size().cast<float>();
// scale to fill screen
float scale = widgetSize.cwiseQuotient(imageSize).minCoeff();
const nanogui::Vector2f scaleFactor = imageSize.cwiseQuotient(screenSize)* scale;
const nanogui::Vector2f scaledImageSize = scale * imageSize;
// center
const nanogui::Vector2f offset = (widgetSize - scaledImageSize) / 2;
const nanogui::Vector2f positionInScreen = absolutePosition().cast<float>() + offset;
const nanogui::Vector2f imagePosition = positionInScreen.cwiseQuotient(screenSize);
mShader.bind();
glEnable(GL_SCISSOR_TEST);
float r = screen()->pixelRatio();
glScissor(positionInScreen.x() * r,
(screenSize.y() - positionInScreen.y() - size().y()) * r,
size().x() * r, size().y() * r);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.texture());
mShader.setUniform("image", 0);
mShader.setUniform("scaleFactor", scaleFactor);
mShader.setUniform("position", imagePosition);
mShader.drawIndexed(GL_TRIANGLES, 0, 2);
glDisable(GL_SCISSOR_TEST);
}
void FrameView::set(const ftl::data::FrameSetPtr &fs, int fid, ftl::codecs::Channel c, cudaStream_t s, bool cp) {
if (!std::atomic_load(&fs_)) {
fid_ = fid;
channel_ = c;
copy_ = cp;
stream_ = s;
std::atomic_store(&fs_, fs); // always set last
}
}
void FrameView::buildShader(ftl::codecs::Channel c) {
switch(c) {
case Channel::Depth:
case Channel::Depth2:
if (mShader.name() != "GL_FV_depth") {
buildFrameViewShader(mShader,
"GL_FV_depth",
GLFrameViewFragmentShaderColorize);
}
vmin_ = 0.0;
vmax_ = 12.0;
break;
case Channel::Colour:
case Channel::Colour2:
case Channel::ColourHighRes:
case Channel::Colour2HighRes:
if (mShader.name() != "GL_FV_rgba") {
buildFrameViewShader(mShader,
"GL_FV_rgba",
GLFrameViewFragmentShader);
}
break;
default:
throw ftl::exception("invalid channel; no shader available");
}
}
#include <opencv2/cudaarithm.hpp>
// copy texture and normalize (if float channel)
template<typename T>
void copyTexture(ftl::cuda::TextureObject<T> &buffer, ftl::gui2::GLTexture &texture, float vmax, float vmin, cudaStream_t s) {
if (buffer.width() == 0 || buffer.height() == 0) {
return;
}
ftl::gui2::GLTexture::Type type;
if (std::is_same<float, T>()) {
type = ftl::gui2::GLTexture::Type::Float;
}
else if(std::is_same<uchar4, T>()) {
type = ftl::gui2::GLTexture::Type::BGRA;
}
else {
// static_assert: see cudaMemcpy2D below
static_assert(std::is_same<uchar4, T>() || std::is_same<float, T>());
}
texture.make(buffer.width(), buffer.height(), type);
auto dst = texture.map(s);
cudaSafeCall(cudaMemcpy2D( dst.data, dst.step, buffer.devicePtr(), buffer.pitch(),
buffer.width()*4, buffer.height(), cudaMemcpyDeviceToDevice));
if (dst.type() == CV_32F) {
auto cvstream = cv::cuda::StreamAccessor::wrapStream(s);
//workaround (shader clamps values to [0,1] range)
cv::cuda::normalize(dst, dst, vmin, vmax, cv::NORM_MINMAX, -1, cv::noArray(), cvstream);
}
texture.unmap(s);
}
void FrameView::set(const NVGcontext *ctx, ftl::rgbd::Frame &frame, ftl::codecs::Channel c, cudaStream_t s, bool cp) {
if (!frame.hasChannel(c)) {
return;
}
buildShader(c);
if (cp) {
if (ftl::codecs::isFloatChannel(c)) {
auto &buffer = frame.createTexture<float>(c, false);
copyTexture<float>(buffer, texture, vmax_, vmin_, s);
}
else {
auto &buffer = frame.createTexture<uchar4>(c, true);
copyTexture<uchar4>(buffer, texture, vmax_, vmin_, s);
}
}
}
#pragma once
#include <atomic>
#include <ftl/rgbd/frame.hpp>
#include <ftl/data/new_frame.hpp>
#include <ftl/data/new_frameset.hpp>
#include <ftl/render/colouriser.hpp>
#include <opencv2/core/opengl.hpp>
#include <ftl/render/colouriser.hpp>
#include <ftl/render/overlay.hpp>
#include <nanogui/widget.h>
#include <nanogui/glutil.h>
#include "../gltexture.hpp"
namespace ftl {
namespace gui2 {
class FrameView : public nanogui::Widget {
private:
GLTexture texture;
nanogui::GLShader mShader;
std::shared_ptr<ftl::data::FrameSet> fs_;
bool flush_ = true;
int fid_ = 0;
ftl::codecs::Channel channel_ = ftl::codecs::Channel::Colour;
cudaStream_t stream_;
bool copy_ = false;
float vmin_ = 0.0f;
float vmax_ = 1.0f;
/** loads a new shader if necessary */
void buildShader(ftl::codecs::Channel c);
public:
FrameView(nanogui::Widget* parent);
virtual void draw(NVGcontext *ctx) override;
/** float frames only: set min/max values */
void setVmax(float v) { vmin_ = v; }
void setVmin(float v) { vmax_ = v; }
float vmax() { return vmax_; }
float vmin() { return vmin_; }
void reset();
/** Set frame. If copy == true, buffer will be copied to OpenGL framebuffer
* at next draw() call. Can be called from any thread. Saves parameters
* and copies the shared pointer. The other set() method is used in next
* draw call to perform the operation. */
void set(const ftl::data::FrameSetPtr& fs, int fid, ftl::codecs::Channel channel, cudaStream_t stream=0, bool copy=true);
/** Set a frame in OpenGL context. May only be called from GUI thread (draw
* or GUI callback */
void set(const NVGcontext *ctx, ftl::rgbd::Frame &f, ftl::codecs::Channel c, cudaStream_t s=0, bool cp=true);
/** should NanoVG draw stack be flushed before drawing the texture? */
void setFlush(bool v) { flush_ = v; }
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment