feat: geometry spreadsheet

This commit is contained in:
parker
2025-08-11 01:48:03 +01:00
28 changed files with 1335 additions and 44 deletions

View File

@@ -31,6 +31,9 @@ set(GUI_SOURCES
src/Gui/main.cpp
src/Gui/Interface.cpp
src/Gui/UtilWidgets/Splitter.cpp
src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.cpp
src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.cpp
src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.cpp
src/Gui/Viewport/Viewport.cpp
src/Gui/Viewport/ViewportGLWidget.cpp
src/Gui/Viewport/GLCamera.cpp

View File

@@ -35,7 +35,7 @@ enzo::nt::OpId enzo::nt::NetworkManager::addOperator(op::OpInfo opInfo)
if(getDisplayOp().has_value() && getDisplayOp().value()==dependentId)
{
cookOp(dependentId);
updateDisplay(dependentOp.getOutputGeo(0));
displayGeoChanged(dependentOp.getOutputGeo(0));
}
}
}
@@ -80,8 +80,8 @@ void enzo::nt::NetworkManager::setDisplayOp(OpId opId)
cookOp(opId);
enzo::nt::GeometryOperator& displayOp = getGeoOperator(opId);
updateDisplay(displayOp.getOutputGeo(0));
displayNodeChanged();
displayGeoChanged(displayOp.getOutputGeo(0));
displayNodeChanged(opId);
}
void enzo::nt::NetworkManager::cookOp(enzo::nt::OpId opId)

View File

@@ -24,7 +24,8 @@ public:
GeometryOperator& getGeoOperator(nt::OpId opId);
void setDisplayOp(OpId opId);
boost::signals2::signal<void ()> displayNodeChanged;
boost::signals2::signal<void (nt::OpId)> displayNodeChanged;
boost::signals2::signal<void (enzo::geo::Geometry& geometry)> displayGeoChanged;
#ifdef UNIT_TEST
void _reset();
@@ -47,9 +48,6 @@ private:
std::optional<OpId> displayOp_=std::nullopt;
Q_SIGNALS:
void updateDisplay(enzo::geo::Geometry& geometry);
};
inline enzo::nt::NetworkManager& nm() {

View File

@@ -15,15 +15,19 @@ ga::Attribute::Attribute(std::string name, ga::AttributeType type)
{
case(AttrType::intT):
intStore_=std::make_shared<std::vector<bt::intT>>();
typeSize_=1;
break;
case(AttrType::floatT):
floatStore_=std::make_shared<std::vector<bt::floatT>>();
typeSize_=1;
break;
case(AttrType::vectorT):
vector3Store_=std::make_shared<std::vector<enzo::bt::Vector3>>();
typeSize_=3;
break;
case(AttrType::boolT):
boolStore_=std::make_shared<std::vector<enzo::bt::boolT>>();
typeSize_=1;
break;
default:
throw std::runtime_error("Type " + std::to_string(static_cast<int>(type_)) + " was not properly accounted for in Attribute constructor");
@@ -32,6 +36,13 @@ ga::Attribute::Attribute(std::string name, ga::AttributeType type)
}
unsigned int ga::Attribute::Attribute::getTypeSize() const
{
return typeSize_;
}
ga::Attribute::Attribute(const Attribute& other)
{
type_ = other.type_;
@@ -39,6 +50,7 @@ ga::Attribute::Attribute(const Attribute& other)
hidden_ = other.hidden_;
readOnly_ = other.readOnly_;
name_ = other.name_;
typeSize_ = other.typeSize_;
switch(type_)
{
@@ -61,12 +73,12 @@ ga::Attribute::Attribute(const Attribute& other)
}
ga::AttributeType ga::Attribute::getType()
ga::AttributeType ga::Attribute::getType() const
{
return type_;
}
std::string ga::Attribute::getName()
std::string ga::Attribute::getName() const
{
return name_;
}

View File

@@ -21,13 +21,16 @@ namespace enzo{
public:
Attribute(std::string name, ga::AttributeType type);
Attribute(const Attribute& other);
AttributeType getType();
std::string getName();
AttributeType getType() const;
std::string getName() const;
unsigned int getTypeSize() const;
template <typename T>
friend class AttributeHandle;
template <typename T>
friend class AttributeHandleRO;
private:
// private attributes are attributes that are hidden from the user
@@ -40,6 +43,7 @@ namespace enzo{
bool readOnly_=false;
ga::AttributeType type_;
unsigned int typeSize_=1;
std::string name_;

View File

@@ -51,14 +51,15 @@ public:
{
throw std::runtime_error("Type " + std::to_string(static_cast<int>(type_)) + " was not properly accounted for in AttributeHandle constructor");
}
}
void addValue(T value)
{
// TODO:make this private (primitive friend classes only)
data_->push_back(value);
}
void reserve(std::size_t newCap)
{
data_->reserve(newCap);
@@ -118,4 +119,94 @@ using AttributeHandleFloat = AttributeHandle<bt::floatT>;
using AttributeHandleVector3 = AttributeHandle<enzo::bt::Vector3>;
using AttributeHandleBool = AttributeHandle<enzo::bt::boolT>;
template <typename T>
class AttributeHandleRO
{
public:
ga::AttributeType type_;
AttributeHandleRO(std::shared_ptr<const Attribute> attribute)
{
type_ = attribute->getType();
// get attribute data pointer
// TODO: check types match
// TODO: add the other types
// int
if constexpr (std::is_same<bt::intT, T>::value)
{
data_=attribute->intStore_;
}
// float
else if constexpr (std::is_same<bt::floatT, T>::value)
{
data_=attribute->floatStore_;
}
// vector 3
else if constexpr (std::is_same<enzo::bt::Vector3, T>::value)
{
data_=attribute->vector3Store_;
}
else if constexpr (std::is_same<enzo::bt::boolT, T>::value)
{
data_=attribute->boolStore_;
}
else
{
throw std::runtime_error("Type " + std::to_string(static_cast<int>(type_)) + " was not properly accounted for in AttributeHandle constructor");
}
}
// TODO: replace with iterator
std::vector<T> getAllValues() const
{
return {data_->begin(), data_->end()};
}
size_t getSize() const
{
return data_->size();
}
T getValue(size_t pos) const
{
// TODO:protect against invalid positions
// TODO: cast types
return (*data_)[pos];
}
std::string getName() const
{
return name_;
}
private:
// private attributes are attributes that are hidden from the user
// for internal use
bool private_=false;
// hidden attributes are user accessible attributes that the user may
// or may want to use
bool hidden_=false;
// allows the user to read the attributeHandle but not modify it
bool readOnly_=false;
std::string name_="";
std::shared_ptr<StoreContainer<T>> data_;
// int typeID_;
};
using AttributeHandleInt = AttributeHandle<bt::intT>;
using AttributeHandleFloat = AttributeHandle<bt::floatT>;
using AttributeHandleVector3 = AttributeHandle<enzo::bt::Vector3>;
using AttributeHandleBool = AttributeHandle<enzo::bt::boolT>;
}

View File

@@ -3,6 +3,7 @@
#include "Engine/Operator/AttributeHandle.h"
#include "Engine/Types.h"
#include <memory>
#include <oneapi/tbb/spin_mutex.h>
#include <oneapi/tbb/task_group.h>
#include <stdexcept>
#include <string>
@@ -33,16 +34,45 @@ geo::Geometry::Geometry(const Geometry& other):
posHandlePoint_{enzo::ga::AttributeHandleVector3(getAttribByName(ga::AttrOwner::POINT, "P"))},
// other
soloPoints_{other.soloPoints_}
soloPoints_{other.soloPoints_},
vertexPrims_{other.vertexPrims_},
primStarts_{other.primStarts_},
primStartsDirty_{other.primStartsDirty_.load()}
{
}
enzo::geo::Geometry& enzo::geo::Geometry::operator=(const enzo::geo::Geometry& rhs) {
if (this == &rhs) return *this;
// attributes
pointAttributes_ = deepCopyAttributes(rhs.pointAttributes_);
vertexAttributes_ = deepCopyAttributes(rhs.vertexAttributes_);
primitiveAttributes_ = deepCopyAttributes(rhs.primitiveAttributes_);
globalAttributes_ = deepCopyAttributes(rhs.globalAttributes_);
// handles
vertexCountHandlePrim_ = enzo::ga::AttributeHandleInt(getAttribByName(ga::AttrOwner::PRIMITIVE, "vertexCount"));
closedHandlePrim_ = enzo::ga::AttributeHandleBool(getAttribByName(ga::AttrOwner::PRIMITIVE, "closed"));
pointOffsetHandleVert_ = enzo::ga::AttributeHandleInt(getAttribByName(ga::AttrOwner::VERTEX, "point"));
posHandlePoint_ = enzo::ga::AttributeHandleVector3(getAttribByName(ga::AttrOwner::POINT, "P"));
// other
soloPoints_ = rhs.soloPoints_;
vertexPrims_ = rhs.vertexPrims_;
primStarts_ = rhs.primStarts_;
primStartsDirty_.store(rhs.primStartsDirty_.load());
return *this;
}
void geo::Geometry::addFace(std::vector<ga::Offset> pointOffsets, bool closed)
{
const ga::Offset primNum = vertexCountHandlePrim_.getSize();
for(ga::Offset pointOffset : pointOffsets)
{
pointOffsetHandleVert_.addValue(pointOffset);
vertexPrims_.push_back(primNum);
soloPoints_.erase(pointOffset);
}
vertexCountHandlePrim_.addValue(pointOffsets.size());
@@ -63,6 +93,36 @@ ga::Offset geo::Geometry::getNumSoloPoints() const
}
// enzo::geo::attributeIterator geo::Geometry::attributesBegin(const ga::AttributeOwner owner)
// {
// return getAttributeStore(owner).begin();
// }
// enzo::geo::attributeIterator geo::Geometry::attributesEnd(const ga::AttributeOwner owner)
// {
// return getAttributeStore(owner).end();
// }
const size_t geo::Geometry::getNumAttributes(const ga::AttributeOwner owner) const
{
return getAttributeStore(owner).size();
}
std::weak_ptr<const ga::Attribute> geo::Geometry::getAttributeByIndex(ga::AttributeOwner owner, unsigned int index) const
{
auto attribStore = getAttributeStore(owner);
if(index>=attribStore.size())
{
throw std::out_of_range("Attribute index out of range: " + std::to_string(index) + " max size: " + std::to_string(attribStore.size()) + "\n");
}
return getAttributeStore(owner)[index];
}
std::set<ga::Offset>::const_iterator geo::Geometry::soloPointsBegin()
{
@@ -89,6 +149,11 @@ bt::Vector3 geo::Geometry::getPointPos(ga::Offset pointOffset) const
return posHandlePoint_.getValue(pointOffset);
}
ga::Offset geo::Geometry::getVertexPrim(ga::Offset vertexOffset) const
{
return vertexPrims_[vertexOffset];
}
void geo::Geometry::setPointPos(const ga::Offset offset, const bt::Vector3& pos)
{
posHandlePoint_.setValue(offset, pos);
@@ -220,19 +285,30 @@ enzo::geo::HeMesh geo::Geometry::computeHalfEdgeMesh()
ga::Offset geo::Geometry::getPrimStartVertex(ga::Offset primOffset) const
{
if(primStartsDirty_.load())
{
tbb::spin_mutex::scoped_lock lock(primStartsMutex_); //lock
if(primStartsDirty_.load()) // double check
{
computePrimStartVertices();
}
}
return primStarts_[primOffset];
}
// TODO: handle this automatically
void geo::Geometry::computePrimStartVertices()
void geo::Geometry::computePrimStartVertices() const
{
const ga::Offset handleSize = vertexCountHandlePrim_.getSize();
primStarts_.clear();
primStarts_.reserve(handleSize);
bt::intT primStart = 0;
for(size_t i=0; i<handleSize; ++i)
{
primStarts_.push_back(primStart);
primStart += vertexCountHandlePrim_.getValue(i);
}
primStartsDirty_.store(false);
}
@@ -265,7 +341,7 @@ ga::AttributeHandle<bt::Vector3> geo::Geometry::addVector3Attribute(ga::Attribut
return ga::AttributeHandle<bt::Vector3>(newAttribute);
}
std::vector<std::shared_ptr<ga::Attribute>>& geo::Geometry::getAttributeStore(ga::AttributeOwner& owner)
geo::Geometry::attribVector& geo::Geometry::getAttributeStore(const ga::AttributeOwner& owner)
{
switch(owner)
{
@@ -286,6 +362,26 @@ std::vector<std::shared_ptr<ga::Attribute>>& geo::Geometry::getAttributeStore(ga
}
}
const geo::Geometry::attribVector& geo::Geometry::getAttributeStore(const ga::AttributeOwner& owner) const
{
switch(owner)
{
case ga::AttributeOwner::POINT:
return pointAttributes_;
break;
case ga::AttributeOwner::VERTEX:
return vertexAttributes_;
break;
case ga::AttributeOwner::PRIMITIVE:
return primitiveAttributes_;
break;
case ga::AttributeOwner::GLOBAL:
return globalAttributes_;
break;
default:
throw std::runtime_error("Unexpected, owner could not be found");
}
}
std::shared_ptr<ga::Attribute> geo::Geometry::getAttribByName(ga::AttributeOwner owner, std::string name)
{

View File

@@ -4,6 +4,9 @@
#include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/Simple_cartesian.h>
#include "Engine/Operator/AttributeHandle.h"
#include <memory>
#include <oneapi/tbb/spin_mutex.h>
#include <tbb/spin_mutex.h>
#include <variant>
@@ -19,16 +22,25 @@ using faceDescriptor = HeMesh::Face_index;
using V_index = HeMesh::Vertex_index;
using F_index = HeMesh::Face_index;
using attributeIterator = std::vector<std::shared_ptr<ga::Attribute>>::iterator;
class Geometry
{
public:
Geometry();
Geometry(const Geometry& other);
Geometry& operator=(const Geometry& rhs);
ga::AttributeHandle<bt::intT> addIntAttribute(ga::AttributeOwner owner, std::string name);
ga::AttributeHandleBool addBoolAttribute(ga::AttributeOwner owner, std::string name);
ga::AttributeHandle<bt::Vector3> addVector3Attribute(ga::AttributeOwner owner, std::string name);
// TODO: return weak ptr
std::shared_ptr<ga::Attribute> getAttribByName(ga::AttributeOwner owner, std::string name);
const size_t getNumAttributes(const ga::AttributeOwner owner) const;
std::weak_ptr<const ga::Attribute> getAttributeByIndex(ga::AttributeOwner owner, unsigned int index) const;
// attributeIterator attributesBegin(const ga::AttributeOwner owner);
// attributeIterator attributesEnd(const ga::AttributeOwner owner);
std::vector<bt::Vector3> derivePointNormals();
HeMesh computeHalfEdgeMesh();
void addFace(std::vector<ga::Offset> pointOffsets, bool closed=true);
@@ -39,21 +51,23 @@ public:
void setPointPos(const ga::Offset offset, const bt::Vector3& pos);
ga::Offset getPrimStartVertex(ga::Offset primOffset) const; // returns the first vertex of the primitive
bt::Vector3 getPosFromVert(ga::Offset vertexOffset) const;
bt::Vector3 getPointPos(ga::Offset pointOffset) const;
unsigned int getPrimVertCount(ga::Offset primOffset) const;
ga::Offset getNumPrims() const;
ga::Offset getNumVerts() const;
ga::Offset getNumPoints() const;
ga::Offset getNumSoloPoints() const;
ga::Offset getPrimStartVertex(ga::Offset primOffset) const; // returns the first vertex of the primitive
bt::Vector3 getPosFromVert(ga::Offset vertexOffset) const;
bt::Vector3 getPointPos(ga::Offset pointOffset) const;
unsigned int getPrimVertCount(ga::Offset primOffset) const;
ga::Offset getVertexPrim(ga::Offset vertexOffset) const;
ga::Offset getNumPrims() const;
ga::Offset getNumVerts() const;
ga::Offset getNumPoints() const;
ga::Offset getNumSoloPoints() const;
bt::boolT isClosed(ga::Offset primOffset) const;
void computePrimStartVertices();
void computePrimStartVertices() const;
private:
using attribVector = std::vector<std::shared_ptr<ga::Attribute>>;
attribVector& getAttributeStore(ga::AttributeOwner& owner);
geo::Geometry::attribVector& getAttributeStore(const ga::AttributeOwner& owner);
const geo::Geometry::attribVector& getAttributeStore(const ga::AttributeOwner& owner) const;
attribVector deepCopyAttributes(attribVector source);
@@ -64,7 +78,11 @@ private:
std::set<ga::Offset> soloPoints_;
std::vector<ga::Offset> primStarts_;
mutable std::vector<ga::Offset> primStarts_;
mutable std::vector<ga::Offset> vertexPrims_;
mutable std::atomic<bool> primStartsDirty_{true};
mutable tbb::spin_mutex primStartsMutex_;
// handles
enzo::ga::AttributeHandleInt vertexCountHandlePrim_;

View File

@@ -124,6 +124,12 @@ std::vector<std::weak_ptr<prm::Parameter>> nt::GeometryOperator::getParameters()
return {parameters_.begin(), parameters_.end()};
}
std::string nt::GeometryOperator::getLabel()
{
return getTypeName();
}
std::vector<std::weak_ptr<const nt::GeometryConnection>> nt::GeometryOperator::getOutputConnections() const
{
return {outputConnections_.begin(), outputConnections_.end()};

View File

@@ -30,6 +30,7 @@ public:
std::vector<std::weak_ptr<prm::Parameter>> getParameters();
std::weak_ptr<prm::Parameter> getParameter(std::string parameterName);
std::string getLabel(); // TODO: implement node labels
std::string getTypeName();
void dirtyNode(bool dirtyDescendents=true);

View File

@@ -0,0 +1,161 @@
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.h"
#include "Engine/Network/NetworkManager.h"
#include "Engine/Operator/GeometryOperator.h"
#include <QLabel>
#include <qpainter.h>
#include <qpushbutton.h>
#include <qwidget.h>
#include <QButtonGroup>
#include <QPainter>
#include <QPaintEvent>
#include <icecream.hpp>
GeoSheetModeButton::GeoSheetModeButton(QWidget* parent)
: QPushButton(parent)
{
setFixedSize(QSize(23,23));
setObjectName("GeoSheetModeButton");
setCheckable(true);
// setStyleSheet(
// R"(
// #GeoSheetModeButton
// {
// background: transparent;
// border: none;
// }
// #GeoSheetModeButton::checked
// {
// opacity: 50;
// border: none;
// }
// )");
}
void GeoSheetModeButton::enterEvent(QEnterEvent *event)
{
hovered_=true;
QPushButton::enterEvent(event);
}
void GeoSheetModeButton::leaveEvent(QEvent *event)
{
hovered_=false;
QPushButton::leaveEvent(event);
}
void GeoSheetModeButton::paintEvent(QPaintEvent* event)
{
const QIcon buttonIcon = icon();
if(!buttonIcon.isNull())
{
QPainter painter(this);
QSize size = iconSize();
if(!isChecked())
{
painter.setOpacity(0.5);
}
if(hovered_)
{
size*=1.1;
}
QPixmap pixmap = buttonIcon.pixmap(size);
QPoint center = rect().center() - QPoint(size.width() / 2, size.height() / 2);
painter.drawPixmap(center, pixmap);
}
}
GeoSheetMenuBarModeSelection::GeoSheetMenuBarModeSelection(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
mainLayout_ = new QHBoxLayout();
constexpr int mainMargin = 0;
mainLayout_->setContentsMargins(mainMargin,mainMargin,mainMargin,mainMargin);
QWidget* buttonBg = new QWidget();
buttonBg->setObjectName("GeoSheetMenuBarButtonBg");
buttonBg->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
constexpr int bgSizeMargin = 5;
buttonBg->setContentsMargins(bgSizeMargin,0,bgSizeMargin,0);
buttonBg->setStyleSheet(
R"(
#GeoSheetMenuBarButtonBg
{
background-color: #242424;
border-radius: 8px;
}
)");
QHBoxLayout* buttonBgLayout = new QHBoxLayout();
constexpr int margin = 0;
buttonBgLayout->setContentsMargins(margin,margin,margin,margin);
modeButtonGroup_.setExclusive(true);
auto newButton = [this, &buttonBgLayout](const char* tooltip="", const char* iconPath=":/icons/attributePoint.svg")
{
auto newButton = new GeoSheetModeButton();
newButton->setToolTip(tooltip);
newButton->setIcon(QIcon(iconPath));
modeButtonGroup_.addButton(newButton);
buttonBgLayout->addWidget(newButton);
return newButton;
};
pointButton = newButton("Point Attributes", ":/icons/attributePoint.svg");
vertexButton = newButton("Vertex Attributes", ":/icons/attributeVertex.svg");
primitiveButton = newButton("Primitive Attributes", ":/icons/attributePrimitive.svg");
globalButton = newButton("Global Attributes", ":/icons/attributeGlobal.svg");
pointButton->setChecked(true);
buttonBg->setLayout(buttonBgLayout);
mainLayout_->addWidget(buttonBg);
setLayout(mainLayout_);
}
GeometrySpreadsheetMenuBar::GeometrySpreadsheetMenuBar(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
mainLayout_ = new QHBoxLayout();
nodeLabel_ = new QLabel();
modeSelection = new GeoSheetMenuBarModeSelection();
mainLayout_->addWidget(nodeLabel_);
mainLayout_->addStretch();
mainLayout_->addWidget(modeSelection);
setProperty("class", "GeometrySpreadsheetMenuBar");
setStyleSheet(
R"(
.GeometrySpreadsheetMenuBar,
.GeometrySpreadsheetMenuBar *
{
background-color: #1B1B1B;
}
)");
constexpr int margins = 5;
mainLayout_->setContentsMargins(margins, margins, margins, margins);
setLayout(mainLayout_);
}
void GeometrySpreadsheetMenuBar::setNode(enzo::nt::OpId opId)
{
enzo::nt::GeometryOperator& geoOp = enzo::nt::nm().getGeoOperator(opId);
nodeLabel_->setText("<b>Node: </b>" + QString::fromStdString(geoOp.getLabel()));
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include "Engine/Operator/OpInfo.h"
#include "Engine/Types.h"
#include <QWidget>
#include <QHBoxLayout>
#include <qbuttongroup.h>
#include <qpushbutton.h>
#include <QLabel>
class GeoSheetModeButton
: public QPushButton
{
public:
GeoSheetModeButton(QWidget *parent = nullptr);
private:
QHBoxLayout* mainLayout_;
protected:
void paintEvent(QPaintEvent *) override;
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
bool hovered_ = false;
};
class GeoSheetMenuBarModeSelection
: public QWidget
{
public:
GeoSheetMenuBarModeSelection(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
GeoSheetModeButton* pointButton;
GeoSheetModeButton* vertexButton;
GeoSheetModeButton* primitiveButton;
GeoSheetModeButton* globalButton;
private:
QHBoxLayout* mainLayout_;
QButtonGroup modeButtonGroup_;
};
class GeometrySpreadsheetMenuBar
: public QWidget
{
public:
GeometrySpreadsheetMenuBar(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
GeoSheetMenuBarModeSelection* modeSelection;
void setNode(enzo::nt::OpId opId);
private:
QHBoxLayout* mainLayout_;
QLabel* nodeLabel_;
};

View File

@@ -0,0 +1,249 @@
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.h"
#include "Engine/Operator/Attribute.h"
#include "Engine/Operator/AttributeHandle.h"
#include "Engine/Operator/Geometry.h"
#include "Engine/Types.h"
#include <icecream.hpp>
#include <memory>
#include <qnamespace.h>
#include <stdexcept>
#include <string>
#include <QBrush>
GeometrySpreadsheetModel::GeometrySpreadsheetModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void GeometrySpreadsheetModel::geometryChanged(enzo::geo::Geometry& geometry)
{
beginResetModel();
geometry_ = geometry;
initBuffers();
endResetModel();
}
void GeometrySpreadsheetModel::initBuffers()
{
// get sizes
const auto attribCount = geometry_.getNumAttributes(attributeOwner_);
attribSizes_.clear();
sectionAttribMap_.clear();
attribSizes_.reserve(attribCount);
for(size_t i=0; i<attribCount; ++i)
{
if(auto attrib = geometry_.getAttributeByIndex(attributeOwner_, i).lock())
{
const auto size = attrib->getTypeSize();
attribSizes_.push_back(size);
for(size_t j=0; j<size; ++j)
{
sectionAttribMap_.push_back(i);
}
}
}
}
void GeometrySpreadsheetModel::setOwner(const enzo::ga::AttributeOwner owner)
{
beginResetModel();
attributeOwner_ = owner;
initBuffers();
endResetModel();
}
int GeometrySpreadsheetModel::rowCount(const QModelIndex &parent) const
{
switch(attributeOwner_)
{
case enzo::ga::AttributeOwner::POINT:
{
return geometry_.getNumPoints();
}
case enzo::ga::AttributeOwner::VERTEX:
{
return geometry_.getNumVerts();
}
case enzo::ga::AttributeOwner::PRIMITIVE:
{
return geometry_.getNumPrims();
}
case enzo::ga::AttributeOwner::GLOBAL:
{
return 1;
}
}
return 1;
}
int GeometrySpreadsheetModel::columnCount(const QModelIndex &parent) const
{
int columnCount = attributeColumnPadding_; // first column is for indices
for(auto size : attribSizes_)
{
columnCount += size;
}
return columnCount;
}
QVariant GeometrySpreadsheetModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
// TODO: reimplement check
// if (index.row() >= geometry_.getNumPoints())
// {
// return QVariant();
// }
if(role == Qt::BackgroundRole && index.column()==0)
{
return QBrush("#1B1B1B");
}
else if (role == Qt::DisplayRole)
{
if(index.column()==0)
{
switch(attributeOwner_)
{
case enzo::ga::AttributeOwner::POINT:
case enzo::ga::AttributeOwner::PRIMITIVE:
{
return index.row();
}
case enzo::ga::AttributeOwner::VERTEX:
{
const enzo::ga::Offset primOffset = geometry_.getVertexPrim(index.row());
const enzo::ga::Offset startVert = geometry_.getPrimStartVertex(primOffset);
const enzo::ga::Offset vertexNumber = index.row()-startVert;
return QString::fromStdString(std::to_string(primOffset)+":"+std::to_string(vertexNumber));
}
case enzo::ga::AttributeOwner::GLOBAL:
{
return "global";
}
}
}
int attributeIndex = indexFromSection(index.column()-attributeColumnPadding_);
if(std::shared_ptr<const enzo::ga::Attribute> attrib = geometry_.getAttributeByIndex(attributeOwner_, attributeIndex).lock())
{
const unsigned int valueIndex = index.column()-attributeIndex-attributeColumnPadding_;
using namespace enzo::ga;
switch(attrib->getType())
{
case(AttributeType::intT):
{
const auto attribHandle = enzo::ga::AttributeHandleRO<enzo::bt::intT>(attrib);
return static_cast<float>(attribHandle.getValue(index.row()));
}
case(AttributeType::floatT):
{
const auto attribHandle = enzo::ga::AttributeHandleRO<enzo::bt::floatT>(attrib);
return attribHandle.getValue(index.row());
}
case(AttributeType::boolT):
{
const auto attribHandle = enzo::ga::AttributeHandleRO<enzo::bt::boolT>(attrib);
return attribHandle.getValue(index.row()) ? "true" : "false";
}
case(AttributeType::vectorT):
{
const auto attribHandle = enzo::ga::AttributeHandleRO<enzo::bt::Vector3>(attrib);
return attribHandle.getValue(index.row())[valueIndex];
}
default:
{
return "Failed";
break;
}
}
}
else
{
throw std::runtime_error("Couldn't lock attribute");
}
// return geometry_.getPointPos(index.row()).x();
}
else
{
return QVariant();
}
}
int GeometrySpreadsheetModel::indexFromSection(unsigned int section) const
{
if(section>=sectionAttribMap_.size())
{
throw std::out_of_range("Section is out of range of sectionAttributMap_, value: " + std::to_string(section) + " expected: <"+std::to_string(sectionAttribMap_.size()));
}
return sectionAttribMap_[section];
}
QVariant GeometrySpreadsheetModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
{
if(section==0) return "Index";
auto attributeIndex = indexFromSection(section-attributeColumnPadding_);
if(auto attrib = geometry_.getAttributeByIndex(attributeOwner_, attributeIndex).lock())
{
if(attribSizes_[attributeIndex]>1)
{
const unsigned int valueIndex = section-attributeIndex-attributeColumnPadding_;
std::string valueIndexString;
if(attrib->getType()==enzo::ga::AttrType::vectorT)
{
valueIndexString = std::array{".x", ".y", ".z", ".w"}.at(valueIndex);
}
else
{
valueIndexString = "["+std::to_string(valueIndex)+"]";
}
return QString::fromStdString(attrib->getName() + valueIndexString);
}
else
{
return QString::fromStdString(attrib->getName());
}
}
else
{
throw std::runtime_error("failed to lock attriubte index");
}
}
else
{
return QStringLiteral("Row %1").arg(section);
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <QAbstractListModel>
#include "Engine/Types.h"
#include "Engine/Operator/Geometry.h"
class GeometrySpreadsheetModel : public QAbstractListModel
{
Q_OBJECT
public:
GeometrySpreadsheetModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
int indexFromSection(unsigned int section) const;
void geometryChanged(enzo::geo::Geometry& geometry);
void setOwner(const enzo::ga::AttributeOwner owner);
void initBuffers();
private:
enzo::nt::OpId opId_;
enzo::geo::Geometry geometry_;
std::vector<unsigned int> attribSizes_;
std::vector<unsigned int> sectionAttribMap_;
const int attributeColumnPadding_ = 1;
enzo::ga::AttributeOwner attributeOwner_=enzo::ga::AttributeOwner::POINT;
};

View File

@@ -0,0 +1,95 @@
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.h"
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.h"
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.h"
#include "Engine/Network/NetworkManager.h"
#include <QTableWidget>
#include <QTreeWidget>
#include <QLabel>
#include <qframe.h>
#include <qpushbutton.h>
#include <qtablewidget.h>
#include <QPainterPath>
GeometrySpreadsheetPanel::GeometrySpreadsheetPanel(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
mainLayout_ = new QVBoxLayout();
mainLayout_->setSpacing(0);
view_ = new QTreeView(parent);
view_->setRootIsDecorated(false);
view_->setAlternatingRowColors(true);
view_->setUniformRowHeights(true); // improves performance
view_->setStyleSheet(R"(
QTreeView {
background-color: #282828;
alternate-background-color: #242424;
paint-alternating-row-colors-for-empty-area: 1;
}
QTreeView QScrollBar {
background: #1B1B1B;
width: 15px;
}
QTreeView QScrollBar::handle:vertical {
background: #282828;
min-height: 50px;
border-radius: 5px;
border-width: 1px;
border-color: #2D2D2D;
border-style: solid;
margin:2px;
}
QTreeView QScrollBar::add-page:vertical,
QTreeView QScrollBar::sub-page:vertical,
QTreeView QScrollBar::add-line:vertical,
QTreeView QScrollBar::sub-line:vertical
{ height: 0px; }
QHeaderView::section {
background-color: #1B1B1B;
}
)");
view_->setFrameStyle(QFrame::NoFrame);
model_ = new GeometrySpreadsheetModel();
view_->setModel(model_);
menuBar_ = new GeometrySpreadsheetMenuBar();
// connect buttons
connect(menuBar_->modeSelection->pointButton, &QPushButton::clicked, this, [this](){model_->setOwner(enzo::ga::AttributeOwner::POINT);});
connect(menuBar_->modeSelection->vertexButton, &QPushButton::clicked, this, [this](){model_->setOwner(enzo::ga::AttributeOwner::VERTEX);});
connect(menuBar_->modeSelection->primitiveButton, &QPushButton::clicked, this, [this](){model_->setOwner(enzo::ga::AttributeOwner::PRIMITIVE);});
connect(menuBar_->modeSelection->globalButton, &QPushButton::clicked, this, [this](){model_->setOwner(enzo::ga::AttributeOwner::GLOBAL);});
// set default
menuBar_->modeSelection->pointButton->click();
mainLayout_->addWidget(menuBar_);
mainLayout_->addWidget(view_);
setLayout(mainLayout_);
}
void GeometrySpreadsheetPanel::setNode(enzo::nt::OpId opId)
{
menuBar_->setNode(opId);
}
void GeometrySpreadsheetPanel::geometryChanged(enzo::geo::Geometry& geometry)
{
model_->geometryChanged(geometry);
view_->update();
}
void GeometrySpreadsheetPanel::resizeEvent(QResizeEvent *event)
{
QPainterPath path;
constexpr float radius = 10;
path.addRoundedRect(mainLayout_->contentsRect(), radius, radius);
QRegion region = QRegion(path.toFillPolygon().toPolygon());
this->setMask(region);
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <QWidget>
#include <QVBoxLayout>
#include <qtreeview.h>
#include "Engine/Types.h"
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.h"
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.h"
class GeometrySpreadsheetPanel
: public QWidget
{
public:
GeometrySpreadsheetPanel(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
public Q_SLOTS:
void geometryChanged(enzo::geo::Geometry& geometry);
void setNode(enzo::nt::OpId opId);
private:
QVBoxLayout* mainLayout_;
QWidget* bgWidget_;
GeometrySpreadsheetModel* model_;
QTreeView* view_;
GeometrySpreadsheetMenuBar* menuBar_;
protected:
void resizeEvent(QResizeEvent *event) override;
};

View File

@@ -1,5 +1,7 @@
#include "Gui/Interface.h"
#include "Engine/Network/NetworkManager.h"
#include "Engine/Operator/Geometry.h"
#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.h"
#include "Gui/ParametersPanel/ParametersPanel.h"
#include "Gui/Viewport/Viewport.h"
#include "Gui/Network/Network.h"
@@ -8,6 +10,7 @@
#include <qsplitter.h>
#include <QTimer>
#include <Gui/UtilWidgets/Splitter.h>
#include <icecream.hpp>
EnzoUI::EnzoUI()
{
@@ -26,35 +29,41 @@ EnzoUI::EnzoUI()
Viewport* viewport = new Viewport();
Network* network = new Network();
ParametersPanel* parametersPanel = new ParametersPanel();
GeometrySpreadsheetPanel* geometrySpreadsheetPanel = new GeometrySpreadsheetPanel();
constexpr int margin = 2;
viewport->layout()->setContentsMargins(margin, margin, margin, margin);
network->layout()->setContentsMargins(margin, margin, margin, margin);
parametersPanel->layout()->setContentsMargins(margin, margin, margin, margin);
geometrySpreadsheetPanel->layout()->setContentsMargins(margin, margin, margin, margin);
mainLayout_->setContentsMargins(margin, margin, margin, margin);
// TODO: dynamic splitters
viewportSplitter_ = new Splitter(this);
networkSplitter_ = new Splitter(this);
spreadsheetSplitter_ = new Splitter(this);
networkSplitter_->setOrientation(Qt::Vertical);
spreadsheetSplitter_->setOrientation(Qt::Vertical);
spreadsheetSplitter_->addWidget(viewport);
spreadsheetSplitter_->addWidget(geometrySpreadsheetPanel);
spreadsheetSplitter_->setSizes({200,100});
viewportSplitter_->addWidget(viewport);
viewportSplitter_->addWidget(spreadsheetSplitter_);
viewportSplitter_->addWidget(networkSplitter_);
viewportSplitter_->setStretchFactor(0, 4);
viewportSplitter_->setStretchFactor(1, 1);
viewportSplitter_->setSizes({100,200});
networkSplitter_->addWidget(parametersPanel);
networkSplitter_->addWidget(network);
networkSplitter_->setStretchFactor(0, 10);
networkSplitter_->setStretchFactor(1, 1);
networkSplitter_->setSizes({40,100});
mainLayout_->addWidget(viewportSplitter_);
// connect signals
connect(&enzo::nt::nm(), &enzo::nt::NetworkManager::updateDisplay, viewport, &Viewport::geometryChanged);
enzo::nt::nm().displayNodeChanged.connect([parametersPanel](){parametersPanel->selectionChanged();});
// connect(&enzo::nt::nm(), &enzo::nt::NetworkManager::updateDisplay, parametersPanel, &ParametersPanel::selectionChanged);
enzo::nt::nm().displayNodeChanged.connect([parametersPanel](enzo::nt::OpId opId){parametersPanel->selectionChanged(opId);});
enzo::nt::nm().displayNodeChanged.connect([geometrySpreadsheetPanel](enzo::nt::OpId opId){geometrySpreadsheetPanel->setNode(opId);});
enzo::nt::nm().displayGeoChanged.connect([geometrySpreadsheetPanel](enzo::geo::Geometry& geometry){geometrySpreadsheetPanel->geometryChanged(geometry);});
enzo::nt::nm().displayGeoChanged.connect([viewport](enzo::geo::Geometry& geometry){viewport->setGeometry(geometry);});
}

View File

@@ -15,6 +15,7 @@ class EnzoUI
QVBoxLayout* viewportSplitLayout_;
Splitter* viewportSplitter_;
Splitter* networkSplitter_;
Splitter* spreadsheetSplitter_;
};

View File

@@ -36,13 +36,11 @@ ParametersPanel::ParametersPanel(QWidget *parent, Qt::WindowFlags f)
setLayout(mainLayout_);
}
void ParametersPanel::selectionChanged()
void ParametersPanel::selectionChanged(enzo::nt::OpId opId)
{
using namespace enzo;
enzo::nt::NetworkManager& nm = enzo::nt::nm();
std::optional<enzo::nt::OpId> displayOpId = nm.getDisplayOp();
if(!displayOpId.has_value()) return;
const enzo::nt::OpId displayOpId = opId;
// clear layout safely
QLayoutItem *child;
@@ -51,7 +49,7 @@ void ParametersPanel::selectionChanged()
delete child;
}
enzo::nt::GeometryOperator& displayOp = nm.getGeoOperator(displayOpId.value());
enzo::nt::GeometryOperator& displayOp = nm.getGeoOperator(displayOpId);
auto parameters = displayOp.getParameters();
std::vector<enzo::ui::AbstractFormParm*> parameterWidgets;

View File

@@ -2,6 +2,7 @@
#include <QWidget>
#include <QVBoxLayout>
#include "Engine/Types.h"
class ParametersPanel
: public QWidget
@@ -9,7 +10,7 @@ class ParametersPanel
public:
ParametersPanel(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
public Q_SLOTS:
void selectionChanged();
void selectionChanged(enzo::nt::OpId opId);
private:
QVBoxLayout* mainLayout_;
QVBoxLayout* parametersLayout_;

View File

@@ -20,7 +20,7 @@ Viewport::Viewport(QWidget *parent, Qt::WindowFlags f)
this->setLayout(mainLayout_);
}
void Viewport::geometryChanged(enzo::geo::Geometry& geometry)
void Viewport::setGeometry(enzo::geo::Geometry& geometry)
{
openGLWidget_->geometryChanged(geometry);
}

View File

@@ -8,6 +8,7 @@ class Viewport
{
public:
Viewport(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
void setGeometry(enzo::geo::Geometry& geometry);
private:
QVBoxLayout* mainLayout_;
ViewportGLWidget* openGLWidget_;
@@ -23,6 +24,4 @@ private:
QPointF leftStartPos_;
bool rightMouseDown_=false;
QPointF rightStartPos_;
public Q_SLOTS:
void geometryChanged(enzo::geo::Geometry& geometry);
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,5 +1,10 @@
<RCC>
<qresource prefix="/">
<file>node-icons/grid.svg</file>
<file>icons/attributePoint.svg</file>
<file>icons/attributeVertex.svg</file>
<file>icons/attributePrimitive.svg</file>
<file>icons/attributeGlobal.svg</file>
<file>icons/attributeBase.svg</file>
</qresource>
</RCC>