Compare commits
4 Commits
bbd7493d9f
...
a8f786d8d1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8f786d8d1 | ||
|
|
add9d37aea | ||
|
|
0bff36f8ba | ||
|
|
cd95f16221 |
@@ -3,10 +3,16 @@ add_executable(gcompose
|
|||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/MainWindow.h
|
src/MainWindow.h
|
||||||
src/MainWindow.cpp
|
src/MainWindow.cpp
|
||||||
src/QViewportPane.h
|
src/ViewportPane.h
|
||||||
src/QViewportPane.cpp
|
src/ViewportPane.cpp
|
||||||
src/MainPanel.h
|
src/MainPanel.h
|
||||||
src/MainPanel.cpp
|
src/MainPanel.cpp
|
||||||
|
src/ContextPanel.h
|
||||||
|
src/ContextPanel.cpp
|
||||||
|
src/ContextModel.h
|
||||||
|
src/ContextModel.cpp
|
||||||
|
src/StyleManager.h
|
||||||
|
src/StyleManager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(gcompose PROPERTIES
|
set_target_properties(gcompose PROPERTIES
|
||||||
|
|||||||
132
app/gcompose/src/ContextModel.cpp
Normal file
132
app/gcompose/src/ContextModel.cpp
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#include "ContextModel.h"
|
||||||
|
#include <QString>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
ContextModel::ContextModel(QObject* parent)
|
||||||
|
: QAbstractItemModel(parent), m_rootContext(nullptr) {}
|
||||||
|
|
||||||
|
ContextModel::~ContextModel() {}
|
||||||
|
|
||||||
|
void ContextModel::setContext(uLib::ObjectsContext* context) {
|
||||||
|
beginResetModel();
|
||||||
|
m_rootContext = context;
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex ContextModel::index(int row, int column, const QModelIndex& parent) const {
|
||||||
|
if (!hasIndex(row, column, parent) || !m_rootContext) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent.isValid()) {
|
||||||
|
if (row < m_rootContext->GetCount()) {
|
||||||
|
return createIndex(row, column, m_rootContext->GetObject(row));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
||||||
|
uLib::ObjectsContext* parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj);
|
||||||
|
if (parentCtx && row < parentCtx->GetCount()) {
|
||||||
|
return createIndex(row, column, parentCtx->GetObject(row));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex ContextModel::parent(const QModelIndex& child) const {
|
||||||
|
if (!child.isValid() || !m_rootContext) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
uLib::Object* childObj = static_cast<uLib::Object*>(child.internalPointer());
|
||||||
|
|
||||||
|
// Finding the parent of childObj is O(N) since there is no parent pointer.
|
||||||
|
// We just do a recursive search starting from root context.
|
||||||
|
std::function<uLib::ObjectsContext*(uLib::ObjectsContext*, uLib::Object*)> findParent =
|
||||||
|
[&findParent](uLib::ObjectsContext* ctx, uLib::Object* target) -> uLib::ObjectsContext* {
|
||||||
|
for (const auto& obj : ctx->GetObjects()) {
|
||||||
|
if (obj == target) return ctx;
|
||||||
|
if (auto subCtx = dynamic_cast<uLib::ObjectsContext*>(obj)) {
|
||||||
|
if (auto p = findParent(subCtx, target)) return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
uLib::ObjectsContext* parentCtx = findParent(m_rootContext, childObj);
|
||||||
|
if (!parentCtx || parentCtx == m_rootContext) {
|
||||||
|
return QModelIndex(); // Root items have invalid parent index
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now need to find the row of parentCtx in its own parent Context.
|
||||||
|
uLib::ObjectsContext* grandParentCtx = findParent(m_rootContext, parentCtx);
|
||||||
|
if (!grandParentCtx) grandParentCtx = m_rootContext;
|
||||||
|
|
||||||
|
int row = -1;
|
||||||
|
for (size_t i = 0; i < grandParentCtx->GetCount(); ++i) {
|
||||||
|
if (grandParentCtx->GetObject(i) == parentCtx) {
|
||||||
|
row = (int)i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row != -1) {
|
||||||
|
return createIndex(row, 0, parentCtx);
|
||||||
|
}
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContextModel::rowCount(const QModelIndex& parent) const {
|
||||||
|
if (!m_rootContext) return 0;
|
||||||
|
|
||||||
|
if (!parent.isValid()) {
|
||||||
|
return m_rootContext->GetCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
||||||
|
if (auto parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj)) {
|
||||||
|
return parentCtx->GetCount();
|
||||||
|
}
|
||||||
|
return 0; // leaf node
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContextModel::columnCount(const QModelIndex& parent) const {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString getDemangledName(const std::type_info& info) {
|
||||||
|
int status = -4;
|
||||||
|
char* demangled = abi::__cxa_demangle(info.name(), nullptr, nullptr, &status);
|
||||||
|
QString res = (status == 0 && demangled) ? QString::fromUtf8(demangled) : QString::fromUtf8(info.name());
|
||||||
|
if (demangled) free(demangled);
|
||||||
|
|
||||||
|
// Remove namespaces
|
||||||
|
int lastColon = res.lastIndexOf("::");
|
||||||
|
if (lastColon != -1) {
|
||||||
|
res = res.mid(lastColon + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "class " prefix if any
|
||||||
|
if (res.startsWith("class ")) res = res.mid(6);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ContextModel::data(const QModelIndex& index, int role) const {
|
||||||
|
if (!index.isValid()) return QVariant();
|
||||||
|
|
||||||
|
uLib::Object* obj = static_cast<uLib::Object*>(index.internalPointer());
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
return getDemangledName(typeid(*obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ContextModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||||
|
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) {
|
||||||
|
return "Object Type";
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
26
app/gcompose/src/ContextModel.h
Normal file
26
app/gcompose/src/ContextModel.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef CONTEXT_MODEL_H
|
||||||
|
#define CONTEXT_MODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include "Core/ObjectsContext.h"
|
||||||
|
|
||||||
|
class ContextModel : public QAbstractItemModel {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ContextModel(QObject* parent = nullptr);
|
||||||
|
virtual ~ContextModel();
|
||||||
|
|
||||||
|
void setContext(uLib::ObjectsContext* context);
|
||||||
|
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
QModelIndex parent(const QModelIndex& child) const override;
|
||||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uLib::ObjectsContext* m_rootContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONTEXT_MODEL_H
|
||||||
43
app/gcompose/src/ContextPanel.cpp
Normal file
43
app/gcompose/src/ContextPanel.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include "ContextPanel.h"
|
||||||
|
#include "ContextModel.h"
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
|
ContextPanel::ContextPanel(QWidget* parent) : QWidget(parent) {
|
||||||
|
m_layout = new QVBoxLayout(this);
|
||||||
|
m_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
m_layout->setSpacing(0);
|
||||||
|
|
||||||
|
// Title bar setup
|
||||||
|
m_titleBar = new QWidget(this);
|
||||||
|
m_titleBar->setObjectName("PaneTitleBar");
|
||||||
|
m_titleBar->setFixedHeight(22);
|
||||||
|
|
||||||
|
auto* titleLayout = new QHBoxLayout(m_titleBar);
|
||||||
|
titleLayout->setContentsMargins(5, 0, 5, 0);
|
||||||
|
|
||||||
|
m_titleLabel = new QLabel("Context Panel", m_titleBar);
|
||||||
|
m_titleLabel->setObjectName("TitleLabel");
|
||||||
|
titleLayout->addWidget(m_titleLabel);
|
||||||
|
titleLayout->addStretch();
|
||||||
|
|
||||||
|
m_layout->addWidget(m_titleBar);
|
||||||
|
|
||||||
|
m_treeView = new QTreeView(this);
|
||||||
|
m_treeView->setObjectName("ContextTree");
|
||||||
|
m_treeView->setHeaderHidden(false);
|
||||||
|
|
||||||
|
m_model = new ContextModel(this);
|
||||||
|
m_treeView->setModel(m_model);
|
||||||
|
|
||||||
|
m_layout->addWidget(m_treeView);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextPanel::~ContextPanel() {}
|
||||||
|
|
||||||
|
void ContextPanel::setContext(uLib::ObjectsContext* context) {
|
||||||
|
m_model->setContext(context);
|
||||||
|
m_treeView->expandAll();
|
||||||
|
}
|
||||||
31
app/gcompose/src/ContextPanel.h
Normal file
31
app/gcompose/src/ContextPanel.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef CONTEXT_PANEL_H
|
||||||
|
#define CONTEXT_PANEL_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QTreeView;
|
||||||
|
class QVBoxLayout;
|
||||||
|
class QLabel;
|
||||||
|
class ContextModel;
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
class ObjectsContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ContextPanel : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ContextPanel(QWidget* parent = nullptr);
|
||||||
|
virtual ~ContextPanel();
|
||||||
|
|
||||||
|
void setContext(uLib::ObjectsContext* context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout* m_layout;
|
||||||
|
QWidget* m_titleBar;
|
||||||
|
QLabel* m_titleLabel;
|
||||||
|
QTreeView* m_treeView;
|
||||||
|
ContextModel* m_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONTEXT_PANEL_H
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
#include "MainPanel.h"
|
#include "MainPanel.h"
|
||||||
#include "QViewportPane.h"
|
#include "ViewportPane.h"
|
||||||
|
#include "ContextPanel.h"
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include "StyleManager.h"
|
||||||
|
|
||||||
MainPanel::MainPanel(QWidget* parent) : QWidget(parent) {
|
MainPanel::MainPanel(QWidget* parent) : QWidget(parent) {
|
||||||
auto* mainLayout = new QVBoxLayout(this);
|
auto* mainLayout = new QVBoxLayout(this);
|
||||||
@@ -13,37 +18,72 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent) {
|
|||||||
|
|
||||||
// 1. Top Menu Panel
|
// 1. Top Menu Panel
|
||||||
auto* menuPanel = new QWidget(this);
|
auto* menuPanel = new QWidget(this);
|
||||||
|
menuPanel->setObjectName("MenuPanel");
|
||||||
menuPanel->setFixedHeight(36);
|
menuPanel->setFixedHeight(36);
|
||||||
menuPanel->setStyleSheet("background-color: #2b2b2b; border-bottom: 1px solid #111;");
|
|
||||||
|
|
||||||
auto* menuLayout = new QHBoxLayout(menuPanel);
|
auto* menuLayout = new QHBoxLayout(menuPanel);
|
||||||
menuLayout->setContentsMargins(10, 0, 10, 0);
|
menuLayout->setContentsMargins(10, 0, 10, 0);
|
||||||
menuLayout->setSpacing(15);
|
menuLayout->setSpacing(15);
|
||||||
|
|
||||||
auto* logo = new QLabel("G-COMPOSE", menuPanel);
|
auto* logo = new QLabel("G-COMPOSE", menuPanel);
|
||||||
logo->setStyleSheet("font-weight: bold; color: #0078d7; font-size: 14px; letter-spacing: 1px;");
|
logo->setObjectName("LogoLabel");
|
||||||
|
|
||||||
auto* btnOpen = new QPushButton("Open", menuPanel);
|
// File Menu Button
|
||||||
auto* btnSave = new QPushButton("Save", menuPanel);
|
auto* btnFile = new QPushButton("File", menuPanel);
|
||||||
|
btnFile->setObjectName("MenuButton");
|
||||||
QString btnStyle = "QPushButton { background: transparent; color: #ccc; border: none; padding: 5px 10px; }"
|
auto* fileMenu = new QMenu(btnFile);
|
||||||
"QPushButton:hover { background: #3c3c3c; color: white; border-radius: 4px; }";
|
fileMenu->addAction("Open", this, &MainPanel::onOpen);
|
||||||
btnOpen->setStyleSheet(btnStyle);
|
fileMenu->addAction("Save", this, &MainPanel::onSave);
|
||||||
btnSave->setStyleSheet(btnStyle);
|
btnFile->setMenu(fileMenu);
|
||||||
|
|
||||||
|
// Theme Menu Button
|
||||||
|
auto* btnTheme = new QPushButton("Theme", menuPanel);
|
||||||
|
btnTheme->setObjectName("MenuButton");
|
||||||
|
auto* themeMenu = new QMenu(btnTheme);
|
||||||
|
themeMenu->addAction("Dark", this, &MainPanel::onDarkTheme);
|
||||||
|
themeMenu->addAction("Bright", this, &MainPanel::onBrightTheme);
|
||||||
|
btnTheme->setMenu(themeMenu);
|
||||||
|
|
||||||
menuLayout->addWidget(logo);
|
menuLayout->addWidget(logo);
|
||||||
menuLayout->addWidget(btnOpen);
|
menuLayout->addWidget(btnFile);
|
||||||
menuLayout->addWidget(btnSave);
|
menuLayout->addWidget(btnTheme);
|
||||||
menuLayout->addStretch();
|
menuLayout->addStretch();
|
||||||
|
|
||||||
mainLayout->addWidget(menuPanel);
|
mainLayout->addWidget(menuPanel);
|
||||||
|
|
||||||
// 2. Central Splitter Area
|
// 2. Central Splitter Area
|
||||||
m_rootSplitter = new QSplitter(Qt::Horizontal, this);
|
m_rootSplitter = new QSplitter(Qt::Horizontal, this);
|
||||||
m_firstPane = new QViewportPane(m_rootSplitter);
|
m_contextPanel = new ContextPanel(m_rootSplitter);
|
||||||
|
m_rootSplitter->addWidget(m_contextPanel);
|
||||||
|
m_firstPane = new ViewportPane(m_rootSplitter);
|
||||||
m_rootSplitter->addWidget(m_firstPane);
|
m_rootSplitter->addWidget(m_firstPane);
|
||||||
|
|
||||||
|
// Set initial sizes
|
||||||
|
QList<int> sizes;
|
||||||
|
sizes << 200 << 1000;
|
||||||
|
m_rootSplitter->setSizes(sizes);
|
||||||
|
|
||||||
mainLayout->addWidget(m_rootSplitter, 1);
|
mainLayout->addWidget(m_rootSplitter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainPanel::setContext(uLib::ObjectsContext* context) {
|
||||||
|
m_contextPanel->setContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainPanel::onOpen() {
|
||||||
|
// Placeholder for open logic
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainPanel::onSave() {
|
||||||
|
// Placeholder for save logic
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainPanel::onDarkTheme() {
|
||||||
|
StyleManager::applyStyle(qApp, "dark");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainPanel::onBrightTheme() {
|
||||||
|
StyleManager::applyStyle(qApp, "bright");
|
||||||
|
}
|
||||||
|
|
||||||
MainPanel::~MainPanel() {}
|
MainPanel::~MainPanel() {}
|
||||||
|
|||||||
@@ -4,7 +4,12 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
class QSplitter;
|
class QSplitter;
|
||||||
class QViewportPane;
|
class ViewportPane;
|
||||||
|
class ContextPanel;
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
class ObjectsContext;
|
||||||
|
}
|
||||||
|
|
||||||
class MainPanel : public QWidget {
|
class MainPanel : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -12,11 +17,19 @@ public:
|
|||||||
explicit MainPanel(QWidget* parent = nullptr);
|
explicit MainPanel(QWidget* parent = nullptr);
|
||||||
virtual ~MainPanel();
|
virtual ~MainPanel();
|
||||||
|
|
||||||
QViewportPane* getFirstPane() const { return m_firstPane; }
|
void setContext(uLib::ObjectsContext* context);
|
||||||
|
ViewportPane* getFirstPane() const { return m_firstPane; }
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onOpen();
|
||||||
|
void onSave();
|
||||||
|
void onDarkTheme();
|
||||||
|
void onBrightTheme();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSplitter* m_rootSplitter;
|
QSplitter* m_rootSplitter;
|
||||||
QViewportPane* m_firstPane;
|
ViewportPane* m_firstPane;
|
||||||
|
ContextPanel* m_contextPanel;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINPANEL_H
|
#endif // MAINPANEL_H
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include "MainPanel.h"
|
#include "MainPanel.h"
|
||||||
|
#include "Core/ObjectsContext.h"
|
||||||
|
|
||||||
using namespace uLib;
|
using namespace uLib;
|
||||||
|
|
||||||
@@ -14,3 +15,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
|
|||||||
|
|
||||||
MainWindow::~MainWindow() {
|
MainWindow::~MainWindow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::setContext(uLib::ObjectsContext* context) {
|
||||||
|
m_panel->setContext(context);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
#include <QVTKOpenGLNativeWidget.h>
|
#include <QVTKOpenGLNativeWidget.h>
|
||||||
|
|
||||||
class MainPanel;
|
class MainPanel;
|
||||||
|
class ViewportPane;
|
||||||
|
|
||||||
namespace uLib {
|
namespace uLib {
|
||||||
namespace Vtk {
|
namespace Vtk {
|
||||||
}
|
}
|
||||||
|
class ObjectsContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainWindow : public QMainWindow {
|
class MainWindow : public QMainWindow {
|
||||||
@@ -17,6 +19,7 @@ public:
|
|||||||
MainWindow(QWidget* parent = nullptr);
|
MainWindow(QWidget* parent = nullptr);
|
||||||
virtual ~MainWindow();
|
virtual ~MainWindow();
|
||||||
|
|
||||||
|
void setContext(uLib::ObjectsContext* context);
|
||||||
MainPanel* getPanel() { return m_panel; }
|
MainPanel* getPanel() { return m_panel; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(null
|
|||||||
|
|
||||||
// Title bar setup
|
// Title bar setup
|
||||||
m_titleBar = new QWidget(this);
|
m_titleBar = new QWidget(this);
|
||||||
|
m_titleBar->setObjectName("PaneTitleBar");
|
||||||
m_titleBar->setFixedHeight(22);
|
m_titleBar->setFixedHeight(22);
|
||||||
m_titleBar->setStyleSheet("background-color: #333; color: white;");
|
|
||||||
|
|
||||||
auto* titleLayout = new QHBoxLayout(m_titleBar);
|
auto* titleLayout = new QHBoxLayout(m_titleBar);
|
||||||
titleLayout->setContentsMargins(5, 0, 5, 0);
|
titleLayout->setContentsMargins(5, 0, 5, 0);
|
||||||
|
|
||||||
m_titleLabel = new QLabel("Viewport", m_titleBar);
|
m_titleLabel = new QLabel("Viewport", m_titleBar);
|
||||||
|
m_titleLabel->setObjectName("TitleLabel");
|
||||||
|
|
||||||
auto* closeBtn = new QToolButton(m_titleBar);
|
auto* closeBtn = new QToolButton(m_titleBar);
|
||||||
|
closeBtn->setObjectName("PaneCloseButton");
|
||||||
closeBtn->setText("X");
|
closeBtn->setText("X");
|
||||||
closeBtn->setFixedSize(18, 18);
|
closeBtn->setFixedSize(18, 18);
|
||||||
closeBtn->setStyleSheet("QToolButton { border: none; font-weight: bold; background: transparent; color: #ccc; } "
|
|
||||||
"QToolButton:hover { color: white; background: red; }");
|
|
||||||
|
|
||||||
titleLayout->addWidget(m_titleLabel);
|
titleLayout->addWidget(m_titleLabel);
|
||||||
titleLayout->addStretch();
|
titleLayout->addStretch();
|
||||||
|
|||||||
44
app/gcompose/src/StyleManager.cpp
Normal file
44
app/gcompose/src/StyleManager.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include "StyleManager.h"
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
static const QString DARK_THEME = R"(
|
||||||
|
QWidget#MenuPanel { background-color: #2b2b2b; border-bottom: 1px solid #111; }
|
||||||
|
QLabel#LogoLabel { font-weight: bold; color: #0078d7; font-size: 14px; letter-spacing: 1px; }
|
||||||
|
QPushButton#MenuButton { background: transparent; color: #ccc; border: none; padding: 5px 10px; }
|
||||||
|
QPushButton#MenuButton:hover { background: #3c3c3c; color: white; border-radius: 4px; }
|
||||||
|
QWidget#PaneTitleBar { background-color: #333; color: white; }
|
||||||
|
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #ccc; }
|
||||||
|
QToolButton#PaneCloseButton:hover { color: white; background: red; }
|
||||||
|
QMenu { background-color: #2b2b2b; color: white; border: 1px solid #111; }
|
||||||
|
QMenu::item:selected { background-color: #3c3c3c; }
|
||||||
|
QTreeView#ContextTree { background-color: #1e1e1e; color: #ccc; border: none; }
|
||||||
|
QTreeView#ContextTree::item:hover { background-color: #2a2d2e; }
|
||||||
|
QTreeView#ContextTree::item:selected { background-color: #094771; color: white; }
|
||||||
|
QHeaderView::section { background-color: #252526; color: #ccc; border: 1px solid #323233; padding: 4px; }
|
||||||
|
)";
|
||||||
|
|
||||||
|
static const QString BRIGHT_THEME = R"(
|
||||||
|
QWidget#MenuPanel { background-color: #f0f0f0; border-bottom: 1px solid #ccc; }
|
||||||
|
QLabel#LogoLabel { font-weight: bold; color: #005a9e; font-size: 14px; letter-spacing: 1px; }
|
||||||
|
QPushButton#MenuButton { background: transparent; color: #333; border: none; padding: 5px 10px; }
|
||||||
|
QPushButton#MenuButton:hover { background: #d0d0d0; color: black; border-radius: 4px; }
|
||||||
|
QWidget#PaneTitleBar { background-color: #e0e0e0; color: black; }
|
||||||
|
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #666; }
|
||||||
|
QToolButton#PaneCloseButton:hover { color: white; background: #e81123; }
|
||||||
|
QMenu { background-color: #f0f0f0; color: black; border: 1px solid #ccc; }
|
||||||
|
QMenu::item:selected { background-color: #d0d0d0; }
|
||||||
|
QTreeView#ContextTree { background-color: #ffffff; color: #333; border: none; }
|
||||||
|
QTreeView#ContextTree::item:hover { background-color: #f2f2f2; }
|
||||||
|
QTreeView#ContextTree::item:selected { background-color: #0078d7; color: white; }
|
||||||
|
QHeaderView::section { background-color: #f3f3f3; color: #333; border: 1px solid #ccc; padding: 4px; }
|
||||||
|
)";
|
||||||
|
|
||||||
|
void StyleManager::applyStyle(QApplication* app, const QString& themeName) {
|
||||||
|
if (!app) return;
|
||||||
|
|
||||||
|
if (themeName == "bright") {
|
||||||
|
app->setStyleSheet(BRIGHT_THEME);
|
||||||
|
} else {
|
||||||
|
app->setStyleSheet(DARK_THEME); // default
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/gcompose/src/StyleManager.h
Normal file
13
app/gcompose/src/StyleManager.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef STYLEMANAGER_H
|
||||||
|
#define STYLEMANAGER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class QApplication;
|
||||||
|
|
||||||
|
class StyleManager {
|
||||||
|
public:
|
||||||
|
static void applyStyle(QApplication* app, const QString& themeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STYLEMANAGER_H
|
||||||
169
app/gcompose/src/ViewportPane.cpp
Normal file
169
app/gcompose/src/ViewportPane.cpp
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#include "ViewportPane.h"
|
||||||
|
#include <Vtk/vtkQViewport.h>
|
||||||
|
#include <Root/QCanvas.h>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <vtkCamera.h>
|
||||||
|
|
||||||
|
ViewportPane::ViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullptr) {
|
||||||
|
m_layout = new QVBoxLayout(this);
|
||||||
|
m_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
m_layout->setSpacing(0);
|
||||||
|
|
||||||
|
// Title bar setup
|
||||||
|
m_titleBar = new QWidget(this);
|
||||||
|
m_titleBar->setObjectName("PaneTitleBar");
|
||||||
|
m_titleBar->setFixedHeight(22);
|
||||||
|
|
||||||
|
auto* titleLayout = new QHBoxLayout(m_titleBar);
|
||||||
|
titleLayout->setContentsMargins(5, 0, 5, 0);
|
||||||
|
|
||||||
|
m_titleLabel = new QLabel("Viewport", m_titleBar);
|
||||||
|
m_titleLabel->setObjectName("TitleLabel");
|
||||||
|
|
||||||
|
auto* closeBtn = new QToolButton(m_titleBar);
|
||||||
|
closeBtn->setObjectName("PaneCloseButton");
|
||||||
|
closeBtn->setText("X");
|
||||||
|
closeBtn->setFixedSize(18, 18);
|
||||||
|
|
||||||
|
titleLayout->addWidget(m_titleLabel);
|
||||||
|
titleLayout->addStretch();
|
||||||
|
titleLayout->addWidget(closeBtn);
|
||||||
|
|
||||||
|
m_layout->addWidget(m_titleBar);
|
||||||
|
|
||||||
|
m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(m_titleBar, &QWidget::customContextMenuRequested, this, &ViewportPane::showContextMenu);
|
||||||
|
connect(closeBtn, &QToolButton::clicked, this, &ViewportPane::onCloseRequested);
|
||||||
|
|
||||||
|
addVtkViewport(); // Initialize with a default VTK viewport
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewportPane::~ViewportPane() {}
|
||||||
|
|
||||||
|
void ViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
||||||
|
if (m_viewport) {
|
||||||
|
m_layout->removeWidget(m_viewport);
|
||||||
|
delete m_viewport;
|
||||||
|
}
|
||||||
|
m_viewport = viewport;
|
||||||
|
m_titleLabel->setText(title);
|
||||||
|
|
||||||
|
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
m_layout->addWidget(m_viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewportPane::addVtkViewport() {
|
||||||
|
auto* viewport = new uLib::Vtk::QViewport(this);
|
||||||
|
setViewport(viewport, "VTK Viewport");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewportPane::addRootCanvas() {
|
||||||
|
auto* canvas = new uLib::Root::QCanvas(this);
|
||||||
|
setViewport(canvas, "ROOT Canvas");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewportPane::onCloseRequested() {
|
||||||
|
QSplitter* parentSplitter = qobject_cast<QSplitter*>(parentWidget());
|
||||||
|
if (parentSplitter && parentSplitter->count() > 1) {
|
||||||
|
deleteLater();
|
||||||
|
} else {
|
||||||
|
// Can't close the last viewport in the splitter safely. Re-initialize to default VTK canvas.
|
||||||
|
addVtkViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewportPane::showContextMenu(const QPoint& pos) {
|
||||||
|
QMenu menu(this);
|
||||||
|
QAction* hSplit = menu.addAction("H split");
|
||||||
|
QAction* vSplit = menu.addAction("V split");
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
bool isVtk = (qobject_cast<uLib::Vtk::QViewport*>(m_viewport) != nullptr);
|
||||||
|
|
||||||
|
QAction* changeType = menu.addAction(isVtk ? "Change to ROOT Canvas" : "Change to VTK Viewport");
|
||||||
|
|
||||||
|
QAction* selected = menu.exec(m_titleBar->mapToGlobal(pos));
|
||||||
|
|
||||||
|
if (selected == hSplit) {
|
||||||
|
AttemptSplit(Qt::Horizontal);
|
||||||
|
} else if (selected == vSplit) {
|
||||||
|
AttemptSplit(Qt::Vertical);
|
||||||
|
} else if (selected == changeType) {
|
||||||
|
if (isVtk) {
|
||||||
|
addRootCanvas();
|
||||||
|
} else {
|
||||||
|
addVtkViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewportPane::AttemptSplit(Qt::Orientation orientation) {
|
||||||
|
QWidget* p = parentWidget();
|
||||||
|
if (!p) return;
|
||||||
|
|
||||||
|
QSplitter* parentSplitter = qobject_cast<QSplitter*>(p);
|
||||||
|
if (!parentSplitter) return;
|
||||||
|
|
||||||
|
ViewportPane* newPane = new ViewportPane();
|
||||||
|
|
||||||
|
// 1. Synchronize viewport content and camera (VTK Viewport only for now)
|
||||||
|
auto* currentVtk = qobject_cast<uLib::Vtk::QViewport*>(m_viewport);
|
||||||
|
if (currentVtk) {
|
||||||
|
auto* newVtk = qobject_cast<uLib::Vtk::QViewport*>(newPane->currentViewport());
|
||||||
|
if (newVtk) {
|
||||||
|
// Copy puppets
|
||||||
|
for (auto* puppet : currentVtk->getPuppets()) {
|
||||||
|
newVtk->AddPuppet(*puppet);
|
||||||
|
}
|
||||||
|
// Copy camera
|
||||||
|
if (currentVtk->GetRenderer() && newVtk->GetRenderer()) {
|
||||||
|
vtkCamera* currentCam = currentVtk->GetRenderer()->GetActiveCamera();
|
||||||
|
vtkCamera* newCam = newVtk->GetRenderer()->GetActiveCamera();
|
||||||
|
if (currentCam && newCam) {
|
||||||
|
newCam->DeepCopy(currentCam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sync grid visible and axis
|
||||||
|
newVtk->SetGridVisible(currentVtk->GetGridVisible());
|
||||||
|
newVtk->SetGridAxis(currentVtk->GetGridAxis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Adjust for ROOT Canvas if that was the active view
|
||||||
|
bool isRoot = (qobject_cast<uLib::Root::QCanvas*>(m_viewport) != nullptr);
|
||||||
|
if (isRoot) {
|
||||||
|
newPane->addRootCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentSplitter->orientation() == orientation) {
|
||||||
|
int index = parentSplitter->indexOf(this);
|
||||||
|
QList<int> sizes = parentSplitter->sizes();
|
||||||
|
int currentSize = sizes.value(index, 0);
|
||||||
|
int half = currentSize / 2;
|
||||||
|
sizes[index] = half;
|
||||||
|
sizes.insert(index + 1, currentSize - half);
|
||||||
|
|
||||||
|
parentSplitter->insertWidget(index + 1, newPane);
|
||||||
|
parentSplitter->setSizes(sizes);
|
||||||
|
} else {
|
||||||
|
int index = parentSplitter->indexOf(this);
|
||||||
|
QList<int> parentSizes = parentSplitter->sizes();
|
||||||
|
|
||||||
|
QSplitter* newSplitter = new QSplitter(orientation);
|
||||||
|
newSplitter->addWidget(this);
|
||||||
|
newSplitter->addWidget(newPane);
|
||||||
|
|
||||||
|
QList<int> subSizes;
|
||||||
|
subSizes << 500 << 500;
|
||||||
|
newSplitter->setSizes(subSizes);
|
||||||
|
|
||||||
|
parentSplitter->insertWidget(index, newSplitter);
|
||||||
|
parentSplitter->setSizes(parentSizes);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/gcompose/src/ViewportPane.h
Normal file
34
app/gcompose/src/ViewportPane.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef VIEWPORTPANE_H
|
||||||
|
#define VIEWPORTPANE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QVBoxLayout;
|
||||||
|
class QLabel;
|
||||||
|
|
||||||
|
class ViewportPane : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ViewportPane(QWidget* parent = nullptr);
|
||||||
|
virtual ~ViewportPane();
|
||||||
|
|
||||||
|
void addVtkViewport();
|
||||||
|
void addRootCanvas();
|
||||||
|
|
||||||
|
QWidget* currentViewport() const { return m_viewport; }
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onCloseRequested();
|
||||||
|
void showContextMenu(const QPoint& pos);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AttemptSplit(Qt::Orientation orientation);
|
||||||
|
void setViewport(QWidget* viewport, const QString& title);
|
||||||
|
|
||||||
|
QVBoxLayout* m_layout;
|
||||||
|
QWidget* m_titleBar;
|
||||||
|
QLabel* m_titleLabel;
|
||||||
|
QWidget* m_viewport;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VIEWPORTPANE_H
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "MainPanel.h"
|
#include "MainPanel.h"
|
||||||
#include "QViewportPane.h"
|
#include "ViewportPane.h"
|
||||||
|
#include "StyleManager.h"
|
||||||
|
|
||||||
#include "Math/ContainerBox.h"
|
#include "Math/ContainerBox.h"
|
||||||
#include <HEP/Geant/Scene.h>
|
#include <HEP/Geant/Scene.h>
|
||||||
@@ -11,6 +12,8 @@
|
|||||||
#include <Vtk/vtkContainerBox.h>
|
#include <Vtk/vtkContainerBox.h>
|
||||||
#include <Vtk/vtkQViewport.h>
|
#include <Vtk/vtkQViewport.h>
|
||||||
|
|
||||||
|
#include "Core/ObjectsContext.h"
|
||||||
|
|
||||||
#include <vtkSmartPointer.h>
|
#include <vtkSmartPointer.h>
|
||||||
#include <vtkCubeSource.h>
|
#include <vtkCubeSource.h>
|
||||||
#include <vtkPolyDataMapper.h>
|
#include <vtkPolyDataMapper.h>
|
||||||
@@ -26,6 +29,7 @@ using namespace uLib::literals;
|
|||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
StyleManager::applyStyle(&app, "dark");
|
||||||
std::cout << "Starting gcompose Qt application..." << std::endl;
|
std::cout << "Starting gcompose Qt application..." << std::endl;
|
||||||
|
|
||||||
ContainerBox world_box(Vector3f(1, 1, 1));
|
ContainerBox world_box(Vector3f(1, 1, 1));
|
||||||
@@ -35,10 +39,15 @@ int main(int argc, char** argv) {
|
|||||||
scene.ConstructWorldBox(world_box.GetSize(), "G4_AIR");
|
scene.ConstructWorldBox(world_box.GetSize(), "G4_AIR");
|
||||||
scene.Initialize();
|
scene.Initialize();
|
||||||
|
|
||||||
|
uLib::ObjectsContext globalContext;
|
||||||
|
globalContext.AddObject(&world_box);
|
||||||
|
globalContext.AddObject(&scene);
|
||||||
|
|
||||||
// 2. Initialize MainWindow (contains embedded VTK QViewport)
|
// 2. Initialize MainWindow (contains embedded VTK QViewport)
|
||||||
MainWindow window;
|
MainWindow window;
|
||||||
|
window.setContext(&globalContext);
|
||||||
MainPanel* panel = window.getPanel();
|
MainPanel* panel = window.getPanel();
|
||||||
QViewportPane* pane = panel->getFirstPane();
|
ViewportPane* pane = panel->getFirstPane();
|
||||||
Vtk::QViewport* viewport = qobject_cast<Vtk::QViewport*>(pane->currentViewport());
|
Vtk::QViewport* viewport = qobject_cast<Vtk::QViewport*>(pane->currentViewport());
|
||||||
|
|
||||||
Vtk::vtkContainerBox vtk_box(&world_box);
|
Vtk::vtkContainerBox vtk_box(&world_box);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ set(HEADERS
|
|||||||
Macros.h
|
Macros.h
|
||||||
Mpl.h
|
Mpl.h
|
||||||
Object.h
|
Object.h
|
||||||
|
ObjectsContext.h
|
||||||
Options.h
|
Options.h
|
||||||
Serializable.h
|
Serializable.h
|
||||||
Signal.h
|
Signal.h
|
||||||
@@ -26,6 +27,7 @@ set(SOURCES
|
|||||||
Archives.cpp
|
Archives.cpp
|
||||||
Debug.cpp
|
Debug.cpp
|
||||||
Object.cpp
|
Object.cpp
|
||||||
|
ObjectsContext.cpp
|
||||||
Options.cpp
|
Options.cpp
|
||||||
Serializable.cpp
|
Serializable.cpp
|
||||||
Signal.cpp
|
Signal.cpp
|
||||||
|
|||||||
47
src/Core/ObjectsContext.cpp
Normal file
47
src/Core/ObjectsContext.cpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include "Core/ObjectsContext.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
|
||||||
|
ObjectsContext::ObjectsContext() : Object() {}
|
||||||
|
|
||||||
|
ObjectsContext::~ObjectsContext() {}
|
||||||
|
|
||||||
|
void ObjectsContext::AddObject(Object* obj) {
|
||||||
|
if (obj && std::find(m_objects.begin(), m_objects.end(), obj) == m_objects.end()) {
|
||||||
|
m_objects.push_back(obj);
|
||||||
|
this->Updated(); // Signal that the context has been updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectsContext::RemoveObject(Object* obj) {
|
||||||
|
auto it = std::find(m_objects.begin(), m_objects.end(), obj);
|
||||||
|
if (it != m_objects.end()) {
|
||||||
|
m_objects.erase(it);
|
||||||
|
this->Updated(); // Signal that the context has been updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectsContext::Clear() {
|
||||||
|
if (!m_objects.empty()) {
|
||||||
|
m_objects.clear();
|
||||||
|
this->Updated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Object*>& ObjectsContext::GetObjects() const {
|
||||||
|
return m_objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ObjectsContext::GetCount() const {
|
||||||
|
return m_objects.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* ObjectsContext::GetObject(size_t index) const {
|
||||||
|
if (index < m_objects.size()) {
|
||||||
|
return m_objects[index];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace uLib
|
||||||
59
src/Core/ObjectsContext.h
Normal file
59
src/Core/ObjectsContext.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef U_CORE_OBJECTS_CONTEXT_H
|
||||||
|
#define U_CORE_OBJECTS_CONTEXT_H
|
||||||
|
|
||||||
|
#include "Core/Object.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ObjectsContext represents a collection of Object instances.
|
||||||
|
*/
|
||||||
|
class ObjectsContext : public Object {
|
||||||
|
public:
|
||||||
|
ObjectsContext();
|
||||||
|
virtual ~ObjectsContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds an object to the context.
|
||||||
|
* @param obj Pointer to the object to add.
|
||||||
|
*/
|
||||||
|
void AddObject(Object* obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes an object from the context.
|
||||||
|
* @param obj Pointer to the object to remove.
|
||||||
|
*/
|
||||||
|
void RemoveObject(Object* obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears all objects from the context.
|
||||||
|
*/
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the collection of objects.
|
||||||
|
* @return Const reference to the vector of object pointers.
|
||||||
|
*/
|
||||||
|
const std::vector<Object*>& GetObjects() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of objects in the context.
|
||||||
|
* @return Size of the collection.
|
||||||
|
*/
|
||||||
|
size_t GetCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an object by index.
|
||||||
|
* @param index The index of the object.
|
||||||
|
* @return Pointer to the object or nullptr if index is out of bounds.
|
||||||
|
*/
|
||||||
|
Object* GetObject(size_t index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Object*> m_objects;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace uLib
|
||||||
|
|
||||||
|
#endif // U_CORE_OBJECTS_CONTEXT_H
|
||||||
@@ -77,13 +77,13 @@ public:
|
|||||||
* @brief Sets the box origin relative to its coordinate system.
|
* @brief Sets the box origin relative to its coordinate system.
|
||||||
* @param v The origin position vector.
|
* @param v The origin position vector.
|
||||||
*/
|
*/
|
||||||
inline void SetOrigin(const Vector3f &v) { m_LocalT.SetPosition(v); }
|
void SetOrigin(const Vector3f &v) { m_LocalT.SetPosition(v); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the box origin relative to its coordinate system.
|
* @brief Gets the box origin relative to its coordinate system.
|
||||||
* @return The origin position vector.
|
* @return The origin position vector.
|
||||||
*/
|
*/
|
||||||
inline Vector3f GetOrigin() const { return m_LocalT.GetPosition(); }
|
Vector3f GetOrigin() const { return m_LocalT.GetPosition(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the size of the box.
|
* @brief Sets the size of the box.
|
||||||
@@ -101,7 +101,7 @@ public:
|
|||||||
* @brief Gets the current size (scale) of the box.
|
* @brief Gets the current size (scale) of the box.
|
||||||
* @return The size vector.
|
* @return The size vector.
|
||||||
*/
|
*/
|
||||||
inline Vector3f GetSize() const {
|
Vector3f GetSize() const {
|
||||||
Vector3f s = this->GetScale();
|
Vector3f s = this->GetScale();
|
||||||
Vector3f ls = m_LocalT.GetScale();
|
Vector3f ls = m_LocalT.GetScale();
|
||||||
return Vector3f(s(0) * ls(0), s(1) * ls(1), s(2) * ls(2));
|
return Vector3f(s(0) * ls(0), s(1) * ls(1), s(2) * ls(2));
|
||||||
@@ -112,7 +112,7 @@ public:
|
|||||||
* @param first Index of the first axis (0=X, 1=Y, 2=Z).
|
* @param first Index of the first axis (0=X, 1=Y, 2=Z).
|
||||||
* @param second Index of the second axis (0=X, 1=Y, 2=Z).
|
* @param second Index of the second axis (0=X, 1=Y, 2=Z).
|
||||||
*/
|
*/
|
||||||
inline void FlipLocalAxes(int first, int second) {
|
void FlipLocalAxes(int first, int second) {
|
||||||
m_LocalT.FlipAxes(first, second);
|
m_LocalT.FlipAxes(first, second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ public:
|
|||||||
* @param v The local point (4D homogeneous vector).
|
* @param v The local point (4D homogeneous vector).
|
||||||
* @return The transformed point in world space.
|
* @return The transformed point in world space.
|
||||||
*/
|
*/
|
||||||
inline Vector4f GetWorldPoint(const Vector4f &v) const {
|
Vector4f GetWorldPoint(const Vector4f &v) const {
|
||||||
return m_LocalT.GetWorldMatrix() * v;
|
return m_LocalT.GetWorldMatrix() * v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ public:
|
|||||||
* @param z Z coordinate in local space.
|
* @param z Z coordinate in local space.
|
||||||
* @return The transformed point in world space.
|
* @return The transformed point in world space.
|
||||||
*/
|
*/
|
||||||
inline Vector4f GetWorldPoint(const float x, const float y, const float z) {
|
Vector4f GetWorldPoint(const float x, const float y, const float z) {
|
||||||
return this->GetWorldPoint(Vector4f(x, y, z, 1));
|
return this->GetWorldPoint(Vector4f(x, y, z, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ public:
|
|||||||
* @param v The world point (4D homogeneous vector).
|
* @param v The world point (4D homogeneous vector).
|
||||||
* @return The transformed point in box-local space.
|
* @return The transformed point in box-local space.
|
||||||
*/
|
*/
|
||||||
inline Vector4f GetLocalPoint(const Vector4f &v) const {
|
Vector4f GetLocalPoint(const Vector4f &v) const {
|
||||||
return m_LocalT.GetWorldMatrix().inverse() * v;
|
return m_LocalT.GetWorldMatrix().inverse() * v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ public:
|
|||||||
* @param z Z coordinate in world space.
|
* @param z Z coordinate in world space.
|
||||||
* @return The transformed point in box-local space.
|
* @return The transformed point in box-local space.
|
||||||
*/
|
*/
|
||||||
inline Vector4f GetLocalPoint(const float x, const float y, const float z) {
|
Vector4f GetLocalPoint(const float x, const float y, const float z) {
|
||||||
return this->GetLocalPoint(Vector4f(x, y, z, 1));
|
return this->GetLocalPoint(Vector4f(x, y, z, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,14 +30,24 @@
|
|||||||
|
|
||||||
#include "Math/Dense.h"
|
#include "Math/Dense.h"
|
||||||
#include "Math/Transform.h"
|
#include "Math/Transform.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace uLib {
|
namespace uLib {
|
||||||
|
|
||||||
class Geometry : public AffineTransform {
|
class Geometry : public AffineTransform {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
virtual Vector3f ToLinear(const Vector3f& curved_space) const {
|
||||||
|
return curved_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Vector3f FromLinear(const Vector3f& cartesian_space) const {
|
||||||
|
return cartesian_space;
|
||||||
|
}
|
||||||
|
|
||||||
inline Vector4f GetWorldPoint(const Vector4f v) const {
|
inline Vector4f GetWorldPoint(const Vector4f v) const {
|
||||||
return this->GetWorldMatrix() * v;
|
Vector3f lin = ToLinear(Vector3f(v.x(), v.y(), v.z()));
|
||||||
|
return this->GetWorldMatrix() * Vector4f(lin.x(), lin.y(), lin.z(), v.w());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Vector4f GetWorldPoint(const float x, const float y, const float z) {
|
inline Vector4f GetWorldPoint(const float x, const float y, const float z) {
|
||||||
@@ -45,7 +55,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Vector4f GetLocalPoint(const Vector4f v) const {
|
inline Vector4f GetLocalPoint(const Vector4f v) const {
|
||||||
return this->GetWorldMatrix().inverse() * v;
|
Vector4f loc_lin = this->GetWorldMatrix().inverse() * v;
|
||||||
|
Vector3f curv = FromLinear(Vector3f(loc_lin.x(), loc_lin.y(), loc_lin.z()));
|
||||||
|
return Vector4f(curv.x(), curv.y(), curv.z(), loc_lin.w());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Vector4f GetLocalPoint(const float x, const float y, const float z) {
|
inline Vector4f GetLocalPoint(const float x, const float y, const float z) {
|
||||||
@@ -53,6 +65,73 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CylindricalGeometry : public Geometry {
|
||||||
|
public:
|
||||||
|
CylindricalGeometry() {}
|
||||||
|
|
||||||
|
Vector3f ToLinear(const Vector3f& cylindrical) const {
|
||||||
|
return Vector3f(cylindrical.x() * std::cos(cylindrical.y()),
|
||||||
|
cylindrical.x() * std::sin(cylindrical.y()),
|
||||||
|
cylindrical.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f FromLinear(const Vector3f& linear) const {
|
||||||
|
float r = std::sqrt(linear.x() * linear.x() + linear.y() * linear.y());
|
||||||
|
float phi = std::atan2(linear.y(), linear.x());
|
||||||
|
return Vector3f(r, phi, linear.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class SphericalGeometry : public Geometry {
|
||||||
|
public:
|
||||||
|
SphericalGeometry() {}
|
||||||
|
|
||||||
|
Vector3f ToLinear(const Vector3f& spherical) const {
|
||||||
|
float r = spherical.x();
|
||||||
|
float theta = spherical.y();
|
||||||
|
float phi = spherical.z();
|
||||||
|
return Vector3f(r * std::sin(theta) * std::cos(phi),
|
||||||
|
r * std::sin(theta) * std::sin(phi),
|
||||||
|
r * std::cos(theta));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f FromLinear(const Vector3f& linear) const {
|
||||||
|
float r = linear.norm();
|
||||||
|
float theta = (r == 0.0f) ? 0.0f : std::acos(linear.z() / r);
|
||||||
|
float phi = std::atan2(linear.y(), linear.x());
|
||||||
|
return Vector3f(r, theta, phi);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ToroidalGeometry : public Geometry {
|
||||||
|
public:
|
||||||
|
ToroidalGeometry(float Rtor) : m_Rtor(Rtor) {}
|
||||||
|
|
||||||
|
Vector3f ToLinear(const Vector3f& toroidal) const {
|
||||||
|
float r = toroidal.x();
|
||||||
|
float theta = toroidal.y();
|
||||||
|
float phi = toroidal.z();
|
||||||
|
return Vector3f((m_Rtor + r * std::cos(theta)) * std::cos(phi),
|
||||||
|
(m_Rtor + r * std::cos(theta)) * std::sin(phi),
|
||||||
|
r * std::sin(theta));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f FromLinear(const Vector3f& linear) const {
|
||||||
|
float phi = std::atan2(linear.y(), linear.x());
|
||||||
|
float r_xy = std::sqrt(linear.x() * linear.x() + linear.y() * linear.y());
|
||||||
|
float delta_r = r_xy - m_Rtor;
|
||||||
|
float z = linear.z();
|
||||||
|
float r = std::sqrt(delta_r * delta_r + z * z);
|
||||||
|
float theta = std::atan2(z, delta_r);
|
||||||
|
return Vector3f(r, theta, phi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_Rtor;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ public:
|
|||||||
|
|
||||||
Eigen::Affine3f& GetTransform() { return m_T; }
|
Eigen::Affine3f& GetTransform() { return m_T; }
|
||||||
|
|
||||||
inline AffineTransform *GetParent() const { return this->m_Parent; }
|
AffineTransform *GetParent() const { return this->m_Parent; }
|
||||||
|
|
||||||
inline void SetParent(AffineTransform *name) { this->m_Parent = name; }
|
void SetParent(AffineTransform *name) { this->m_Parent = name; }
|
||||||
|
|
||||||
inline void SetMatrix (Matrix4f mat) { m_T.matrix() = mat; }
|
void SetMatrix (Matrix4f mat) { m_T.matrix() = mat; }
|
||||||
inline Matrix4f GetMatrix() const { return m_T.matrix(); }
|
Matrix4f GetMatrix() const { return m_T.matrix(); }
|
||||||
|
|
||||||
Matrix4f GetWorldMatrix() const
|
Matrix4f GetWorldMatrix() const
|
||||||
{
|
{
|
||||||
@@ -94,45 +94,45 @@ public:
|
|||||||
else return m_Parent->GetWorldMatrix() * m_T.matrix(); // T = B * A //
|
else return m_Parent->GetWorldMatrix() * m_T.matrix(); // T = B * A //
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SetPosition(const Vector3f v) { this->m_T.translation() = v; }
|
void SetPosition(const Vector3f v) { this->m_T.translation() = v; }
|
||||||
|
|
||||||
inline Vector3f GetPosition() const { return this->m_T.translation(); }
|
Vector3f GetPosition() const { return this->m_T.translation(); }
|
||||||
|
|
||||||
inline void SetRotation(const Matrix3f m) { this->m_T.linear() = m; }
|
void SetRotation(const Matrix3f m) { this->m_T.linear() = m; }
|
||||||
|
|
||||||
inline Matrix3f GetRotation() const { return this->m_T.rotation(); }
|
Matrix3f GetRotation() const { return this->m_T.rotation(); }
|
||||||
|
|
||||||
inline void Translate(const Vector3f v) { this->m_T.pretranslate(v); }
|
void Translate(const Vector3f v) { this->m_T.pretranslate(v); }
|
||||||
|
|
||||||
inline void Scale(const Vector3f v) { this->m_T.scale(v); }
|
void Scale(const Vector3f v) { this->m_T.scale(v); }
|
||||||
|
|
||||||
inline Vector3f GetScale() const {
|
Vector3f GetScale() const {
|
||||||
return Vector3f(m_T.linear().col(0).norm(),
|
return Vector3f(m_T.linear().col(0).norm(),
|
||||||
m_T.linear().col(1).norm(),
|
m_T.linear().col(1).norm(),
|
||||||
m_T.linear().col(2).norm());
|
m_T.linear().col(2).norm());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void Rotate(const Matrix3f m) { this->m_T.rotate(m); }
|
void Rotate(const Matrix3f m) { this->m_T.rotate(m); }
|
||||||
|
|
||||||
inline void Rotate(const float angle, Vector3f axis)
|
void Rotate(const float angle, Vector3f axis)
|
||||||
{
|
{
|
||||||
axis.normalize(); // prehaps not necessary ( see eigens )
|
axis.normalize(); // prehaps not necessary ( see eigens )
|
||||||
Eigen::AngleAxisf ax(angle,axis);
|
Eigen::AngleAxisf ax(angle,axis);
|
||||||
this->m_T.rotate(Eigen::Quaternion<float>(ax));
|
this->m_T.rotate(Eigen::Quaternion<float>(ax));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Rotate(const Vector3f euler_axis) {
|
void Rotate(const Vector3f euler_axis) {
|
||||||
float angle = euler_axis.norm();
|
float angle = euler_axis.norm();
|
||||||
Rotate(angle,euler_axis);
|
Rotate(angle,euler_axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void PreRotate(const Matrix3f m) { this->m_T.prerotate(m); }
|
void PreRotate(const Matrix3f m) { this->m_T.prerotate(m); }
|
||||||
|
|
||||||
inline void QuaternionRotate(const Vector4f q)
|
void QuaternionRotate(const Vector4f q)
|
||||||
{ this->m_T.rotate(Eigen::Quaternion<float>(q)); }
|
{ this->m_T.rotate(Eigen::Quaternion<float>(q)); }
|
||||||
|
|
||||||
inline void EulerYZYRotate(const Vector3f e) {
|
void EulerYZYRotate(const Vector3f e) {
|
||||||
Matrix3f mat;
|
Matrix3f mat;
|
||||||
mat = Eigen::AngleAxisf(e.x(), Vector3f::UnitY())
|
mat = Eigen::AngleAxisf(e.x(), Vector3f::UnitY())
|
||||||
* Eigen::AngleAxisf(e.y(), Vector3f::UnitZ())
|
* Eigen::AngleAxisf(e.y(), Vector3f::UnitZ())
|
||||||
@@ -140,7 +140,7 @@ public:
|
|||||||
m_T.rotate(mat);
|
m_T.rotate(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void FlipAxes(int first, int second)
|
void FlipAxes(int first, int second)
|
||||||
{
|
{
|
||||||
Matrix3f mat = Matrix3f::Identity();
|
Matrix3f mat = Matrix3f::Identity();
|
||||||
mat.col(first).swap(mat.col(second));
|
mat.col(first).swap(mat.col(second));
|
||||||
|
|||||||
@@ -162,6 +162,36 @@ int main()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ // test parent-child relationship
|
||||||
|
ContainerBox Parent;
|
||||||
|
Parent.SetPosition(Vector3f(1,1,1));
|
||||||
|
Parent.Scale(Vector3f(2,2,2));
|
||||||
|
|
||||||
|
ContainerBox Child;
|
||||||
|
Child.SetParent(&Parent);
|
||||||
|
Child.SetPosition(Vector3f(0,0,0));
|
||||||
|
|
||||||
|
HPoint3f pt = Child.GetLocalPoint(HPoint3f(0,0,0));
|
||||||
|
HPoint3f wp = Child.GetWorldPoint(pt);
|
||||||
|
TEST0( Vector4f0(wp - HPoint3f(0,0,0)) );
|
||||||
|
|
||||||
|
pt = HPoint3f(1,1,1);
|
||||||
|
wp = Child.GetWorldPoint(pt);
|
||||||
|
TEST0( Vector4f0(wp - HPoint3f(3,3,3)) );
|
||||||
|
|
||||||
|
Parent.Rotate(Vector3f(M_PI,0,0));
|
||||||
|
pt = HPoint3f(1,1,1);
|
||||||
|
wp = Child.GetWorldPoint(pt);
|
||||||
|
TEST0( Vector4f0(wp - HPoint3f(3,-1,-1)) );
|
||||||
|
|
||||||
|
Child.Rotate(Vector3f(M_PI,0,0));
|
||||||
|
pt = HPoint3f(1,1,1);
|
||||||
|
wp = Child.GetWorldPoint(pt);
|
||||||
|
TEST0( Vector4f0(wp - HPoint3f(3,3,3)) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
END_TESTING;
|
END_TESTING;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,36 @@ int main()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// CYLINDRICAL GEOMETRY TESTING
|
||||||
|
{
|
||||||
|
CylindricalGeometry cyl;
|
||||||
|
Vector3f cyl_pt(5.0f, M_PI_2, 3.0f);
|
||||||
|
Vector3f lin = cyl.ToLinear(cyl_pt);
|
||||||
|
TEST0( Vector4f0(lin.homogeneous() - HPoint3f(0.0f, 5.0f, 3.0f)) );
|
||||||
|
Vector3f recovered = cyl.FromLinear(lin);
|
||||||
|
TEST0( Vector4f0(recovered.homogeneous() - cyl_pt.homogeneous()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// SPHERICAL GEOMETRY TESTING
|
||||||
|
{
|
||||||
|
SphericalGeometry sph;
|
||||||
|
Vector3f sph_pt(5.0f, M_PI_2, M_PI);
|
||||||
|
Vector3f lin = sph.ToLinear(sph_pt);
|
||||||
|
TEST0( Vector4f0(lin.homogeneous() - HPoint3f(-5.0f, 0.0f, 0.0f)) );
|
||||||
|
Vector3f recovered = sph.FromLinear(Vector3f(-5.0f, 0.0f, 0.0f));
|
||||||
|
TEST0( Vector4f0(recovered.homogeneous() - sph_pt.homogeneous()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOROIDAL GEOMETRY TESTING
|
||||||
|
{
|
||||||
|
ToroidalGeometry tor(10.0f);
|
||||||
|
Vector3f tor_pt(1.0f, M_PI_2, M_PI);
|
||||||
|
Vector3f lin = tor.ToLinear(tor_pt);
|
||||||
|
TEST0( Vector4f0(lin.homogeneous() - HPoint3f(-10.0f, 0.0f, 1.0f)) );
|
||||||
|
Vector3f recovered = tor.FromLinear(Vector3f(-10.0f, 0.0f, 1.0f));
|
||||||
|
TEST0( Vector4f0(recovered.homogeneous() - tor_pt.homogeneous()) );
|
||||||
|
}
|
||||||
|
|
||||||
END_TESTING;
|
END_TESTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user