From 24495caf3681ae1313f9036574ef7d670dc1a8cb Mon Sep 17 00:00:00 2001 From: toloudis Date: Fri, 10 May 2024 14:15:00 -0700 Subject: [PATCH] more buttons! --- agave_app/ViewToolbar.cpp | 232 ++++++++++++++++++++++++++++++++-- agave_app/ViewToolbar.h | 9 +- agave_app/agaveGui.cpp | 46 +++++-- agave_app/agaveGui.h | 11 +- agave_app/agaveGui.qrc | 1 + agave_app/icons/frameView.svg | 7 + renderlib/CCamera.cpp | 75 +++++++++++ renderlib/CCamera.h | 76 +---------- 8 files changed, 361 insertions(+), 96 deletions(-) create mode 100644 agave_app/icons/frameView.svg diff --git a/agave_app/ViewToolbar.cpp b/agave_app/ViewToolbar.cpp index 9fd2b891..e4a8bf34 100644 --- a/agave_app/ViewToolbar.cpp +++ b/agave_app/ViewToolbar.cpp @@ -1,9 +1,190 @@ #include "ViewToolbar.h" +#include #include #include +#include +#include #include +#include #include +#include + +#if 0 +class PaletteIconEngine : public QIconEngine +{ +public: + PaletteIconEngine(); + PaletteIconEngine(const PaletteIconEngine& other); + ~PaletteIconEngine(); + + void paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) override; + QPixmap pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) override; + + void addFile(const QString& fileName, const QSize& size, QIcon::Mode mode, QIcon::State state) override; + + QString key() const override; + QIconEngine* clone() const override; + + QList availableSizes(QIcon::Mode mode, QIcon::State state) override; + + void virtual_hook(int id, void* data) override; + +private: + QScopedPointer renderer_; + QString src_file_; +}; + +static QString +actualFilename(const QString& filename) +{ + if (QFile::exists(filename)) + return filename; + + QString fn = filename.mid(0, filename.lastIndexOf('.')); + if (QFile::exists(fn)) + return fn; + + return fn + ".svg"; +} + +static QColor +getIconColor(QIcon::Mode mode, QIcon::State state) +{ + Q_UNUSED(state); + QPalette::ColorGroup color_group = QPalette::Active; + if (mode == QIcon::Disabled) + color_group = QPalette::Disabled; + return QPalette().color(color_group, QPalette::WindowText); +} + +static QPixmap +renderIcon(QSvgRenderer* renderer, const QSize& size, const QBrush& brush) +{ + QPixmap output(size); + output.fill(Qt::transparent); + + QPainter p(&output); + renderer->render(&p); + + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + + p.setPen(Qt::NoPen); + p.setBrush(brush); + + p.drawRect(output.rect()); + + return output; +} + +PaletteIconEngine::PaletteIconEngine() +{ + renderer_.reset(new QSvgRenderer()); +} + +PaletteIconEngine::PaletteIconEngine(const PaletteIconEngine& other) + : QIconEngine(other) +{ + src_file_ = other.src_file_; + renderer_.reset(new QSvgRenderer()); + if (other.renderer_->isValid()) + renderer_->load(other.src_file_); +} + +PaletteIconEngine::~PaletteIconEngine() = default; + +void +PaletteIconEngine::paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) +{ + // "direct rendereng" using given painter is not possible + // because colorization logic modifies already painted area + // such behavior is not acceptable, so render icon to pixmap first + QSize size = rect.size() * painter->device()->devicePixelRatioF(); + QPixmap pxm = pixmap(size, mode, state); + // set device pixel ratio exactly before painting, + // this will allow to reuse the same cached pixmap + pxm.setDevicePixelRatio(painter->device()->devicePixelRatioF()); + painter->drawPixmap(rect, pxm); +} + +QPixmap +PaletteIconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) +{ + // unfortunately, default implementation (call paint() on newly created QPixmap) + // doesn't initialize (fill) QPixmap with transparent color, so artifacts may happen + // so, it is better to implement pixmap() function and use it in paint() implementation + QColor color = getIconColor(mode, state); + QString pmckey = + QString("pie_%1:%2x%3:%4").arg(src_file_).arg(size.width()).arg(size.height()).arg(color.name(QColor::HexArgb)); + QPixmap pxm; + if (!QPixmapCache::find(pmckey, &pxm)) { + pxm = renderIcon(renderer_.data(), size, color); + QPixmapCache::insert(pmckey, pxm); + } + return pxm; +} + +void +PaletteIconEngine::addFile(const QString& fileName, const QSize& size, QIcon::Mode mode, QIcon::State state) +{ + Q_UNUSED(size); + Q_UNUSED(mode); + Q_UNUSED(state); + QString filename = actualFilename(fileName); + if (filename == src_file_) + return; + if (renderer_->load(filename)) + src_file_ = filename; +} + +QString +PaletteIconEngine::key() const +{ + return QLatin1String("palette"); +} + +QIconEngine* +PaletteIconEngine::clone() const +{ + return new PaletteIconEngine(*this); +} + +QList +PaletteIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) +{ + Q_UNUSED(mode); + Q_UNUSED(state); + QList sizes; + sizes << QSize(512, 512); // just workaround to make tray icon visible on KDE + return sizes; +} + +void +PaletteIconEngine::virtual_hook(int id, void* data) +{ + switch (id) { + case QIconEngine::IsNullHook: + *reinterpret_cast(data) = (!renderer_ || !renderer_->isValid()); + break; + + default: + QIconEngine::virtual_hook(id, data); + } +} + +class SwitchingIcon : public QIcon +{ + SwitchingIcon(const QString& file_name) + : QIcon() + { + // QString f1 = QString("switch1/%1").arg(file_name); + // QString f2 = QString("switch2/%1").arg(file_name); + QIconEngine* engine = new PaletteIconEngine(); + engine->addFile(file_name, QSize(512, 512), QIcon::Normal, QIcon::Off); + QIcon(engine); + } +}; +#endif ViewToolbar::ViewToolbar(QWidget* parent) : QWidget(parent) @@ -12,26 +193,61 @@ ViewToolbar::ViewToolbar(QWidget* parent) toolbarLayout->setSpacing(0); toolbarLayout->setContentsMargins(0, 0, 0, 0); + frameViewButton = new QPushButton(QIcon(":/icons/frameView.svg"), "", this); + frameViewButton->setToolTip(QString("Frame view")); + frameViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + frameViewButton->adjustSize(); + frameViewButton->setFocusPolicy(Qt::NoFocus); + toolbarLayout->addWidget(frameViewButton); + + orthoViewButton = new QPushButton(QIcon(":/icons/orthoView.svg"), "", this); + orthoViewButton->setToolTip(QString("Ortho/Persp view")); + orthoViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + orthoViewButton->adjustSize(); + orthoViewButton->setFocusPolicy(Qt::NoFocus); + toolbarLayout->addWidget(orthoViewButton); + topViewButton = new QPushButton(QIcon(":/icons/topView.svg"), "", this); - topViewButton->setToolTip(QString("Top view")); + topViewButton->setToolTip(QString("Top view (-Y)")); topViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); topViewButton->adjustSize(); topViewButton->setFocusPolicy(Qt::NoFocus); toolbarLayout->addWidget(topViewButton); + bottomViewButton = new QPushButton(QIcon(":/icons/topView.svg"), "", this); + bottomViewButton->setToolTip(QString("Bottom view (+Y)")); + bottomViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + bottomViewButton->adjustSize(); + bottomViewButton->setFocusPolicy(Qt::NoFocus); + toolbarLayout->addWidget(bottomViewButton); + frontViewButton = new QPushButton(QIcon(":/icons/frontView.svg"), "", this); - frontViewButton->setToolTip(QString("Front view")); + frontViewButton->setToolTip(QString("Front view (-Z)")); frontViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); frontViewButton->adjustSize(); frontViewButton->setFocusPolicy(Qt::NoFocus); toolbarLayout->addWidget(frontViewButton); - sideViewButton = new QPushButton(QIcon(":/icons/leftView.svg"), "", this); - sideViewButton->setToolTip(QString("Side view")); - sideViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - sideViewButton->adjustSize(); - sideViewButton->setFocusPolicy(Qt::NoFocus); - toolbarLayout->addWidget(sideViewButton); + backViewButton = new QPushButton(QIcon(":/icons/frontView.svg"), "", this); + backViewButton->setToolTip(QString("Back view (+Z)")); + backViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + backViewButton->adjustSize(); + backViewButton->setFocusPolicy(Qt::NoFocus); + toolbarLayout->addWidget(backViewButton); + + leftViewButton = new QPushButton(QIcon(":/icons/leftView.svg"), "", this); + leftViewButton->setToolTip(QString("Left view (+X)")); + leftViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + leftViewButton->adjustSize(); + leftViewButton->setFocusPolicy(Qt::NoFocus); + toolbarLayout->addWidget(leftViewButton); + + rightViewButton = new QPushButton(QIcon(":/icons/leftView.svg"), "", this); + rightViewButton->setToolTip(QString("Right view (-X)")); + rightViewButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + rightViewButton->adjustSize(); + rightViewButton->setFocusPolicy(Qt::NoFocus); + toolbarLayout->addWidget(rightViewButton); // int width = zoomFitButton->fontMetrics().boundingRect(zoomFitButton->text()).width() * 2 + 7; // zoomInButton->setMaximumWidth(width); diff --git a/agave_app/ViewToolbar.h b/agave_app/ViewToolbar.h index 9c010dc1..de59a666 100644 --- a/agave_app/ViewToolbar.h +++ b/agave_app/ViewToolbar.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include class ViewToolbar : public QWidget { @@ -10,9 +10,14 @@ class ViewToolbar : public QWidget ViewToolbar(QWidget* parent = nullptr); virtual ~ViewToolbar(); + QPushButton* frameViewButton; + QPushButton* orthoViewButton; QPushButton* topViewButton; + QPushButton* bottomViewButton; QPushButton* frontViewButton; - QPushButton* sideViewButton; + QPushButton* backViewButton; + QPushButton* leftViewButton; + QPushButton* rightViewButton; void positionToolbar(); }; diff --git a/agave_app/agaveGui.cpp b/agave_app/agaveGui.cpp index 29de2881..b89e9ee5 100644 --- a/agave_app/agaveGui.cpp +++ b/agave_app/agaveGui.cpp @@ -77,8 +77,11 @@ agaveGui::agaveGui(QWidget* parent) vlayout->setContentsMargins(0, 0, 0, 0); auto toolbar = new ViewToolbar(); connect(toolbar->topViewButton, &QPushButton::clicked, this, &agaveGui::view_top); - connect(toolbar->sideViewButton, &QPushButton::clicked, this, &agaveGui::view_side); + connect(toolbar->bottomViewButton, &QPushButton::clicked, this, &agaveGui::view_bottom); + connect(toolbar->leftViewButton, &QPushButton::clicked, this, &agaveGui::view_left); + connect(toolbar->rightViewButton, &QPushButton::clicked, this, &agaveGui::view_right); connect(toolbar->frontViewButton, &QPushButton::clicked, this, &agaveGui::view_front); + connect(toolbar->backViewButton, &QPushButton::clicked, this, &agaveGui::view_back); vlayout->addWidget(toolbar); vlayout->addWidget(m_glView, 1); @@ -202,15 +205,15 @@ agaveGui::createActions() this->m_glView->toggleAreaLightRotateControls(); }); - m_cameraTopViewAction = new QAction(tr("&Top view"), this); - m_cameraTopViewAction->setStatusTip(tr("Set camera to top view")); - connect(m_cameraTopViewAction, SIGNAL(triggered()), this, SLOT(view_top())); - m_cameraFrontViewAction = new QAction(tr("&Front view"), this); - m_cameraFrontViewAction->setStatusTip(tr("Set camera to front view")); - connect(m_cameraFrontViewAction, SIGNAL(triggered()), this, SLOT(view_front())); - m_cameraSideViewAction = new QAction(tr("&Side view"), this); - m_cameraSideViewAction->setStatusTip(tr("Set camera to side view")); - connect(m_cameraSideViewAction, SIGNAL(triggered()), this, SLOT(view_side())); + // m_cameraTopViewAction = new QAction(tr("&Top view"), this); + // m_cameraTopViewAction->setStatusTip(tr("Set camera to top view")); + // connect(m_cameraTopViewAction, SIGNAL(triggered()), this, SLOT(view_top())); + // m_cameraFrontViewAction = new QAction(tr("&Front view"), this); + // m_cameraFrontViewAction->setStatusTip(tr("Set camera to front view")); + // connect(m_cameraFrontViewAction, SIGNAL(triggered()), this, SLOT(view_front())); + // m_cameraSideViewAction = new QAction(tr("&Side view"), this); + // m_cameraSideViewAction->setStatusTip(tr("Set camera to side view")); + // connect(m_cameraSideViewAction, SIGNAL(triggered()), this, SLOT(view_side())); } void @@ -816,6 +819,13 @@ agaveGui::view_top() RenderSettings* rs = m_glView->borrowRenderer()->m_renderSettings; rs->m_DirtyFlags.SetFlag(CameraDirty); } +void +agaveGui::view_bottom() +{ + m_glView->borrowRenderer()->m_CCamera.SetViewMode(ViewModeBottom); + RenderSettings* rs = m_glView->borrowRenderer()->m_renderSettings; + rs->m_DirtyFlags.SetFlag(CameraDirty); +} void agaveGui::view_front() @@ -824,14 +834,28 @@ agaveGui::view_front() RenderSettings* rs = m_glView->borrowRenderer()->m_renderSettings; rs->m_DirtyFlags.SetFlag(CameraDirty); } +void +agaveGui::view_back() +{ + m_glView->borrowRenderer()->m_CCamera.SetViewMode(ViewModeBack); + RenderSettings* rs = m_glView->borrowRenderer()->m_renderSettings; + rs->m_DirtyFlags.SetFlag(CameraDirty); +} void -agaveGui::view_side() +agaveGui::view_left() { m_glView->borrowRenderer()->m_CCamera.SetViewMode(ViewModeLeft); RenderSettings* rs = m_glView->borrowRenderer()->m_renderSettings; rs->m_DirtyFlags.SetFlag(CameraDirty); } +void +agaveGui::view_right() +{ + m_glView->borrowRenderer()->m_CCamera.SetViewMode(ViewModeRight); + RenderSettings* rs = m_glView->borrowRenderer()->m_renderSettings; + rs->m_DirtyFlags.SetFlag(CameraDirty); +} void agaveGui::view_toggleProjection() diff --git a/agave_app/agaveGui.h b/agave_app/agaveGui.h index 2ebcec93..8812803e 100644 --- a/agave_app/agaveGui.h +++ b/agave_app/agaveGui.h @@ -44,8 +44,11 @@ class agaveGui : public QMainWindow public slots: void view_top(); + void view_bottom(); void view_front(); - void view_side(); + void view_back(); + void view_left(); + void view_right(); private slots: void open(); void openDirectory(); @@ -120,9 +123,9 @@ private slots: QAction* m_sourceCodeAction = nullptr; QAction* m_citationAction = nullptr; QAction* m_toggleRotateControlsAction = nullptr; - QAction* m_cameraTopViewAction = nullptr; - QAction* m_cameraFrontViewAction = nullptr; - QAction* m_cameraSideViewAction = nullptr; + // QAction* m_cameraTopViewAction = nullptr; + // QAction* m_cameraFrontViewAction = nullptr; + // QAction* m_cameraSideViewAction = nullptr; QSlider* createAngleSlider(); QSlider* createRangeSlider(); diff --git a/agave_app/agaveGui.qrc b/agave_app/agaveGui.qrc index a7290d4a..783b77ce 100644 --- a/agave_app/agaveGui.qrc +++ b/agave_app/agaveGui.qrc @@ -9,5 +9,6 @@ icons/topView.svg icons/leftView.svg icons/orthoView.svg + icons/frameView.svg diff --git a/agave_app/icons/frameView.svg b/agave_app/icons/frameView.svg new file mode 100644 index 00000000..7b7a70c5 --- /dev/null +++ b/agave_app/icons/frameView.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/renderlib/CCamera.cpp b/renderlib/CCamera.cpp index 5441df85..f8c5f9ea 100644 --- a/renderlib/CCamera.cpp +++ b/renderlib/CCamera.cpp @@ -2,6 +2,81 @@ #include "gesture/gesture.h" +void +CCamera::SetViewMode(const EViewMode ViewMode) +{ + if (ViewMode == ViewModeUser) + return; + + glm::vec3 ctr = m_SceneBoundingBox.GetCenter(); + m_Target = ctr; + m_Up = glm::vec3(0.0f, 1.0f, 0.0f); + + const float size = m_SceneBoundingBox.GetDiagonalLength(); + const float Length = (m_Projection == ORTHOGRAPHIC) ? 2.0f : size * 0.5f / tan(0.5f * m_FovV * DEG_TO_RAD); + m_OrthoScale = DEF_ORTHO_SCALE; + + // const float Distance = 0.866f; + // const float Length = Distance * m_SceneBoundingBox.GetMaxLength(); + + m_From = m_Target; + + switch (ViewMode) { + case ViewModeFront: + m_From.z += Length; + m_Up = glm::vec3(0.0f, 1.0f, 0.0f); + break; + case ViewModeBack: + m_From.z -= Length; + m_Up = glm::vec3(0.0f, 1.0f, 0.0f); + break; + case ViewModeLeft: + m_From.x += Length; + m_Up = glm::vec3(0.0f, 0.0f, 1.0f); + break; + case ViewModeRight: + m_From.x -= Length; + m_Up = glm::vec3(0.0f, 0.0f, 1.0f); + break; + case ViewModeTop: + m_From.y += Length; + m_Up = glm::vec3(0.0f, 0.0f, 1.0f); + break; + case ViewModeBottom: + m_From.y -= Length; + m_Up = glm::vec3(0.0f, 0.0f, 1.0f); + break; + case ViewModeIsometricFrontLeftTop: + m_From = glm::vec3(Length, Length, -Length); + break; + case ViewModeIsometricFrontRightTop: + m_From = m_Target + glm::vec3(-Length, Length, -Length); + break; + case ViewModeIsometricFrontLeftBottom: + m_From = m_Target + glm::vec3(Length, -Length, -Length); + break; + case ViewModeIsometricFrontRightBottom: + m_From = m_Target + glm::vec3(-Length, -Length, -Length); + break; + case ViewModeIsometricBackLeftTop: + m_From = m_Target + glm::vec3(Length, Length, Length); + break; + case ViewModeIsometricBackRightTop: + m_From = m_Target + glm::vec3(-Length, Length, Length); + break; + case ViewModeIsometricBackLeftBottom: + m_From = m_Target + glm::vec3(Length, -Length, Length); + break; + case ViewModeIsometricBackRightBottom: + m_From = m_Target + glm::vec3(-Length, -Length, Length); + break; + default: + break; + } + + Update(); +} + LinearSpace3f CCamera::getFrame() const { diff --git a/renderlib/CCamera.h b/renderlib/CCamera.h index a5cd862b..2d5bec31 100644 --- a/renderlib/CCamera.h +++ b/renderlib/CCamera.h @@ -505,75 +505,7 @@ class CCamera Update(); } - void SetViewMode(const EViewMode ViewMode) - { - if (ViewMode == ViewModeUser) - return; - - glm::vec3 ctr = m_SceneBoundingBox.GetCenter(); - m_Target = ctr; - m_Up = glm::vec3(0.0f, 1.0f, 0.0f); - - const float size = m_SceneBoundingBox.GetDiagonalLength(); - const float Length = (m_Projection == ORTHOGRAPHIC) ? 2.0f : size * 0.5f / tan(0.5f * m_FovV * DEG_TO_RAD); - m_OrthoScale = DEF_ORTHO_SCALE; - - // const float Distance = 0.866f; - // const float Length = Distance * m_SceneBoundingBox.GetMaxLength(); - - m_From = m_Target; - - switch (ViewMode) { - case ViewModeFront: - m_From.z += Length; - break; - case ViewModeBack: - m_From.z -= Length; - break; - case ViewModeLeft: - m_From.x += Length; - break; - case ViewModeRight: - m_From.x -= -Length; - break; - case ViewModeTop: - m_From.y += Length; - m_Up = glm::vec3(0.0f, 0.0f, 1.0f); - break; - case ViewModeBottom: - m_From.y -= -Length; - m_Up = glm::vec3(0.0f, 0.0f, -1.0f); - break; - case ViewModeIsometricFrontLeftTop: - m_From = glm::vec3(Length, Length, -Length); - break; - case ViewModeIsometricFrontRightTop: - m_From = m_Target + glm::vec3(-Length, Length, -Length); - break; - case ViewModeIsometricFrontLeftBottom: - m_From = m_Target + glm::vec3(Length, -Length, -Length); - break; - case ViewModeIsometricFrontRightBottom: - m_From = m_Target + glm::vec3(-Length, -Length, -Length); - break; - case ViewModeIsometricBackLeftTop: - m_From = m_Target + glm::vec3(Length, Length, Length); - break; - case ViewModeIsometricBackRightTop: - m_From = m_Target + glm::vec3(-Length, Length, Length); - break; - case ViewModeIsometricBackLeftBottom: - m_From = m_Target + glm::vec3(Length, -Length, Length); - break; - case ViewModeIsometricBackRightBottom: - m_From = m_Target + glm::vec3(-Length, -Length, Length); - break; - default: - break; - } - - Update(); - } + void SetViewMode(const EViewMode ViewMode); float getHalfHorizontalAperture() const { return tan(this->GetHorizontalFOV_radians() * 0.5f); } @@ -632,7 +564,8 @@ struct CameraModifier CameraModifier() : nearClip(0) , farClip(0) - {} + { + } }; inline CameraModifier @@ -648,7 +581,8 @@ operator+(const CameraModifier& a, const CameraModifier& b) return c; } -inline CameraModifier operator*(const CameraModifier& a, const float b) +inline CameraModifier +operator*(const CameraModifier& a, const float b) { CameraModifier c; c.position = a.position * b;