diff --git a/CMakeLists.txt b/CMakeLists.txt index ad55f02..f151d34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/Engine/Network/NetworkManager.cpp b/src/Engine/Network/NetworkManager.cpp index 655d825..9bd3824 100644 --- a/src/Engine/Network/NetworkManager.cpp +++ b/src/Engine/Network/NetworkManager.cpp @@ -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) diff --git a/src/Engine/Network/NetworkManager.h b/src/Engine/Network/NetworkManager.h index 52e52e4..f90b935 100644 --- a/src/Engine/Network/NetworkManager.h +++ b/src/Engine/Network/NetworkManager.h @@ -24,7 +24,8 @@ public: GeometryOperator& getGeoOperator(nt::OpId opId); void setDisplayOp(OpId opId); - boost::signals2::signal displayNodeChanged; + boost::signals2::signal displayNodeChanged; + boost::signals2::signal displayGeoChanged; #ifdef UNIT_TEST void _reset(); @@ -47,9 +48,6 @@ private: std::optional displayOp_=std::nullopt; -Q_SIGNALS: - void updateDisplay(enzo::geo::Geometry& geometry); - }; inline enzo::nt::NetworkManager& nm() { diff --git a/src/Engine/Operator/Attribute.cpp b/src/Engine/Operator/Attribute.cpp index 637aa72..9849f19 100644 --- a/src/Engine/Operator/Attribute.cpp +++ b/src/Engine/Operator/Attribute.cpp @@ -15,15 +15,19 @@ ga::Attribute::Attribute(std::string name, ga::AttributeType type) { case(AttrType::intT): intStore_=std::make_shared>(); + typeSize_=1; break; case(AttrType::floatT): floatStore_=std::make_shared>(); + typeSize_=1; break; case(AttrType::vectorT): vector3Store_=std::make_shared>(); + typeSize_=3; break; case(AttrType::boolT): boolStore_=std::make_shared>(); + typeSize_=1; break; default: throw std::runtime_error("Type " + std::to_string(static_cast(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_; } diff --git a/src/Engine/Operator/Attribute.h b/src/Engine/Operator/Attribute.h index 8f4c6ad..23eb096 100644 --- a/src/Engine/Operator/Attribute.h +++ b/src/Engine/Operator/Attribute.h @@ -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 friend class AttributeHandle; + template + 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_; diff --git a/src/Engine/Operator/AttributeHandle.h b/src/Engine/Operator/AttributeHandle.h index 0178409..6f67901 100644 --- a/src/Engine/Operator/AttributeHandle.h +++ b/src/Engine/Operator/AttributeHandle.h @@ -51,14 +51,15 @@ public: { throw std::runtime_error("Type " + std::to_string(static_cast(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; using AttributeHandleVector3 = AttributeHandle; using AttributeHandleBool = AttributeHandle; +template +class AttributeHandleRO +{ +public: + ga::AttributeType type_; + + AttributeHandleRO(std::shared_ptr attribute) + { + type_ = attribute->getType(); + // get attribute data pointer + // TODO: check types match + // TODO: add the other types + + // int + if constexpr (std::is_same::value) + { + data_=attribute->intStore_; + } + + // float + else if constexpr (std::is_same::value) + { + data_=attribute->floatStore_; + } + + // vector 3 + else if constexpr (std::is_same::value) + { + data_=attribute->vector3Store_; + } + else if constexpr (std::is_same::value) + { + data_=attribute->boolStore_; + } + else + { + throw std::runtime_error("Type " + std::to_string(static_cast(type_)) + " was not properly accounted for in AttributeHandle constructor"); + } + + } + + // TODO: replace with iterator + std::vector 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> data_; + + // int typeID_; + +}; + +using AttributeHandleInt = AttributeHandle; +using AttributeHandleFloat = AttributeHandle; +using AttributeHandleVector3 = AttributeHandle; +using AttributeHandleBool = AttributeHandle; + + } diff --git a/src/Engine/Operator/Geometry.cpp b/src/Engine/Operator/Geometry.cpp index a3e1cdc..a75b0cf 100644 --- a/src/Engine/Operator/Geometry.cpp +++ b/src/Engine/Operator/Geometry.cpp @@ -3,6 +3,7 @@ #include "Engine/Operator/AttributeHandle.h" #include "Engine/Types.h" #include +#include #include #include #include @@ -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 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 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::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 geo::Geometry::addVector3Attribute(ga::Attribut return ga::AttributeHandle(newAttribute); } -std::vector>& geo::Geometry::getAttributeStore(ga::AttributeOwner& owner) +geo::Geometry::attribVector& geo::Geometry::getAttributeStore(const ga::AttributeOwner& owner) { switch(owner) { @@ -286,6 +362,26 @@ std::vector>& 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 geo::Geometry::getAttribByName(ga::AttributeOwner owner, std::string name) { diff --git a/src/Engine/Operator/Geometry.h b/src/Engine/Operator/Geometry.h index 5d9516a..1f8f649 100644 --- a/src/Engine/Operator/Geometry.h +++ b/src/Engine/Operator/Geometry.h @@ -4,6 +4,9 @@ #include #include #include "Engine/Operator/AttributeHandle.h" +#include +#include +#include #include @@ -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>::iterator; + class Geometry { public: Geometry(); Geometry(const Geometry& other); + Geometry& operator=(const Geometry& rhs); ga::AttributeHandle addIntAttribute(ga::AttributeOwner owner, std::string name); ga::AttributeHandleBool addBoolAttribute(ga::AttributeOwner owner, std::string name); ga::AttributeHandle addVector3Attribute(ga::AttributeOwner owner, std::string name); // TODO: return weak ptr std::shared_ptr getAttribByName(ga::AttributeOwner owner, std::string name); + + const size_t getNumAttributes(const ga::AttributeOwner owner) const; + std::weak_ptr getAttributeByIndex(ga::AttributeOwner owner, unsigned int index) const; + // attributeIterator attributesBegin(const ga::AttributeOwner owner); + // attributeIterator attributesEnd(const ga::AttributeOwner owner); + std::vector derivePointNormals(); HeMesh computeHalfEdgeMesh(); void addFace(std::vector 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>; - 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 soloPoints_; - std::vector primStarts_; + mutable std::vector primStarts_; + mutable std::vector vertexPrims_; + + mutable std::atomic primStartsDirty_{true}; + mutable tbb::spin_mutex primStartsMutex_; // handles enzo::ga::AttributeHandleInt vertexCountHandlePrim_; diff --git a/src/Engine/Operator/GeometryOperator.cpp b/src/Engine/Operator/GeometryOperator.cpp index cbfd0d7..b2ad040 100644 --- a/src/Engine/Operator/GeometryOperator.cpp +++ b/src/Engine/Operator/GeometryOperator.cpp @@ -124,6 +124,12 @@ std::vector> nt::GeometryOperator::getParameters() return {parameters_.begin(), parameters_.end()}; } +std::string nt::GeometryOperator::getLabel() +{ + return getTypeName(); +} + + std::vector> nt::GeometryOperator::getOutputConnections() const { return {outputConnections_.begin(), outputConnections_.end()}; diff --git a/src/Engine/Operator/GeometryOperator.h b/src/Engine/Operator/GeometryOperator.h index ea4c364..9dddca0 100644 --- a/src/Engine/Operator/GeometryOperator.h +++ b/src/Engine/Operator/GeometryOperator.h @@ -30,6 +30,7 @@ public: std::vector> getParameters(); std::weak_ptr getParameter(std::string parameterName); + std::string getLabel(); // TODO: implement node labels std::string getTypeName(); void dirtyNode(bool dirtyDescendents=true); diff --git a/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.cpp b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.cpp new file mode 100644 index 0000000..1c66af4 --- /dev/null +++ b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.cpp @@ -0,0 +1,161 @@ +#include "Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.h" +#include "Engine/Network/NetworkManager.h" +#include "Engine/Operator/GeometryOperator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +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("Node: " + QString::fromStdString(geoOp.getLabel())); +} diff --git a/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.h b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.h new file mode 100644 index 0000000..094e756 --- /dev/null +++ b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetMenuBar.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Engine/Operator/OpInfo.h" +#include "Engine/Types.h" +#include +#include +#include +#include +#include + +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_; +}; + diff --git a/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.cpp b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.cpp new file mode 100644 index 0000000..2e87189 --- /dev/null +++ b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.cpp @@ -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 +#include +#include +#include +#include +#include + + +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; igetTypeSize(); + attribSizes_.push_back(size); + + for(size_t j=0; j= 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 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(attrib); + return static_cast(attribHandle.getValue(index.row())); + } + case(AttributeType::floatT): + { + const auto attribHandle = enzo::ga::AttributeHandleRO(attrib); + return attribHandle.getValue(index.row()); + } + case(AttributeType::boolT): + { + const auto attribHandle = enzo::ga::AttributeHandleRO(attrib); + return attribHandle.getValue(index.row()) ? "true" : "false"; + } + case(AttributeType::vectorT): + { + const auto attribHandle = enzo::ga::AttributeHandleRO(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); + } +} diff --git a/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.h b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.h new file mode 100644 index 0000000..500b790 --- /dev/null +++ b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetModel.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#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 attribSizes_; + std::vector sectionAttribMap_; + const int attributeColumnPadding_ = 1; + enzo::ga::AttributeOwner attributeOwner_=enzo::ga::AttributeOwner::POINT; + +}; diff --git a/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.cpp b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.cpp new file mode 100644 index 0000000..5377c3c --- /dev/null +++ b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +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); +} + diff --git a/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.h b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.h new file mode 100644 index 0000000..f89285d --- /dev/null +++ b/src/Gui/GeometrySpreadsheetPanel/GeometrySpreadsheetPanel.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#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; + + +}; diff --git a/src/Gui/Interface.cpp b/src/Gui/Interface.cpp index 66baf14..16381ad 100644 --- a/src/Gui/Interface.cpp +++ b/src/Gui/Interface.cpp @@ -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 #include #include +#include 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);}); } diff --git a/src/Gui/Interface.h b/src/Gui/Interface.h index f84180e..7355022 100644 --- a/src/Gui/Interface.h +++ b/src/Gui/Interface.h @@ -15,6 +15,7 @@ class EnzoUI QVBoxLayout* viewportSplitLayout_; Splitter* viewportSplitter_; Splitter* networkSplitter_; + Splitter* spreadsheetSplitter_; }; diff --git a/src/Gui/ParametersPanel/ParametersPanel.cpp b/src/Gui/ParametersPanel/ParametersPanel.cpp index 83040d0..518a939 100644 --- a/src/Gui/ParametersPanel/ParametersPanel.cpp +++ b/src/Gui/ParametersPanel/ParametersPanel.cpp @@ -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 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 parameterWidgets; diff --git a/src/Gui/ParametersPanel/ParametersPanel.h b/src/Gui/ParametersPanel/ParametersPanel.h index 5a99de1..8ed9755 100644 --- a/src/Gui/ParametersPanel/ParametersPanel.h +++ b/src/Gui/ParametersPanel/ParametersPanel.h @@ -2,6 +2,7 @@ #include #include +#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_; diff --git a/src/Gui/Viewport/Viewport.cpp b/src/Gui/Viewport/Viewport.cpp index 746f374..b2aa903 100644 --- a/src/Gui/Viewport/Viewport.cpp +++ b/src/Gui/Viewport/Viewport.cpp @@ -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); } diff --git a/src/Gui/Viewport/Viewport.h b/src/Gui/Viewport/Viewport.h index fe86376..88868c1 100644 --- a/src/Gui/Viewport/Viewport.h +++ b/src/Gui/Viewport/Viewport.h @@ -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); }; diff --git a/static/icons/attributeBase.svg b/static/icons/attributeBase.svg new file mode 100644 index 0000000..7b27707 --- /dev/null +++ b/static/icons/attributeBase.svg @@ -0,0 +1,76 @@ + + + + diff --git a/static/icons/attributeGlobal.svg b/static/icons/attributeGlobal.svg new file mode 100644 index 0000000..83a219c --- /dev/null +++ b/static/icons/attributeGlobal.svg @@ -0,0 +1,76 @@ + + + + diff --git a/static/icons/attributePoint.svg b/static/icons/attributePoint.svg new file mode 100644 index 0000000..c496ad0 --- /dev/null +++ b/static/icons/attributePoint.svg @@ -0,0 +1,81 @@ + + + + diff --git a/static/icons/attributePrimitive.svg b/static/icons/attributePrimitive.svg new file mode 100644 index 0000000..59fbcdd --- /dev/null +++ b/static/icons/attributePrimitive.svg @@ -0,0 +1,76 @@ + + + + diff --git a/static/icons/attributeVertex.svg b/static/icons/attributeVertex.svg new file mode 100644 index 0000000..84c013b --- /dev/null +++ b/static/icons/attributeVertex.svg @@ -0,0 +1,121 @@ + + + + diff --git a/static/resources.qrc b/static/resources.qrc index e25bec4..21592b0 100644 --- a/static/resources.qrc +++ b/static/resources.qrc @@ -1,5 +1,10 @@ node-icons/grid.svg + icons/attributePoint.svg + icons/attributeVertex.svg + icons/attributePrimitive.svg + icons/attributeGlobal.svg + icons/attributeBase.svg