diff --git a/CMakeLists.txt b/CMakeLists.txt index e8ffce0..09456e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ qt_add_executable(${AppExec} src/Engine/Operator/Attribute.cpp src/Engine/Operator/Geometry.cpp src/Engine/Operator/GeometryOperator.cpp + src/Engine/Operator/GeometryConnection.cpp src/Engine/Network/NetworkManager.cpp ) @@ -63,6 +64,7 @@ add_executable(${TestExec} src/Engine/Operator/Geometry.cpp src/Engine/Operator/GeometryOperator.cpp src/Engine/Network/NetworkManager.cpp + src/Engine/Operator/GeometryConnection.cpp ) find_package(Catch2 3 REQUIRED) target_link_libraries(${TestExec} PRIVATE Catch2::Catch2WithMain Eigen3::Eigen) diff --git a/src/Engine/Operator/GeometryConnection.cpp b/src/Engine/Operator/GeometryConnection.cpp new file mode 100644 index 0000000..55c0170 --- /dev/null +++ b/src/Engine/Operator/GeometryConnection.cpp @@ -0,0 +1,12 @@ +#include "Engine/Operator/GeometryConnection.h" + +enzo::nt::GeometryConnection::GeometryConnection(enzo::nt::OpId inputOpId, unsigned int inputIndex, enzo::nt::OpId outputOpId, unsigned int outputIndex) +:inputOperatorId_{inputOpId}, inputIndex_{inputIndex}, outputOperatorId_{outputOpId}, outputIndex_{outputIndex} +{ +} + +enzo::nt::OpId enzo::nt::GeometryConnection::getInputOpId() const {return inputOperatorId_; } +enzo::nt::OpId enzo::nt::GeometryConnection::getOutputOpId() const {return outputOperatorId_; } +unsigned int enzo::nt::GeometryConnection::getInputIndex() const {return inputIndex_; } +unsigned int enzo::nt::GeometryConnection::getOutputIndex() const {return outputIndex_; } + diff --git a/src/Engine/Operator/GeometryConnection.h b/src/Engine/Operator/GeometryConnection.h new file mode 100644 index 0000000..dcfa922 --- /dev/null +++ b/src/Engine/Operator/GeometryConnection.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Engine/Types.h" +namespace enzo::nt +{ +class GeometryOperator; +class GeometryConnection +{ +public: + // input and output are in relation to data flow + // the input node is the node the data flows from + // the output node is the node the data flows to + GeometryConnection(enzo::nt::OpId inputOpId, unsigned int inputIndex, enzo::nt::OpId outputOpId, unsigned int outputIndex); + + enzo::nt::OpId getInputOpId() const; + enzo::nt::OpId getOutputOpId() const; + unsigned int getInputIndex() const; + unsigned int getOutputIndex() const; +private: + enzo::nt::OpId inputOperatorId_; + enzo::nt::OpId outputOperatorId_; + unsigned int inputIndex_; + unsigned int outputIndex_; +}; +} diff --git a/src/Engine/Operator/GeometryOperator.cpp b/src/Engine/Operator/GeometryOperator.cpp index 5d570d4..ba94dfb 100644 --- a/src/Engine/Operator/GeometryOperator.cpp +++ b/src/Engine/Operator/GeometryOperator.cpp @@ -1,55 +1,93 @@ #include "Engine/Operator/GeometryOperator.h" +#include +#include "Engine/Network/NetworkManager.h" #include +#include using namespace enzo; +void enzo::nt::connectOperators(enzo::nt::OpId inputOpId, unsigned int inputIndex, enzo::nt::OpId outputOpId, unsigned int outputIndex) +{ + // get network manager + auto nm = enzo::nt::NetworkManager::getInstance(); + + auto inputOp = nm->getGeoOperator(inputOpId); + auto outputOp = nm->getGeoOperator(outputOpId); + + auto newConnection = std::make_shared(inputOpId, inputIndex, outputOpId, outputIndex); + + // set output on the upper operator + outputOp.addOutputConnection(newConnection); + + // set input on the lower operator + inputOp.addInputConnection(newConnection); +} + nt::GeometryOperator::GeometryOperator() { // TODO: drive by geometry definition maxInputs_=4; maxOutputs_=4; - - inputIds_ = std::vector>(maxInputs_, std::nullopt); - outputIds_ = std::vector>(maxOutputs_, std::nullopt); } -bool nt::GeometryOperator::setInput(unsigned int inputNumber, nt::OpId opId) +// bool nt::GeometryOperator::setInput(unsigned int inputNumber, nt::OpId opId) +// { +// if(inputNumber>=maxInputs_) +// { +// return false; +// } +// inputIds_[inputNumber] = opId; + +// return true; +// } +// bool nt::GeometryOperator::setOutput(unsigned int outputNumber, nt::OpId opId) +// { + +// if(outputNumber>=maxOutputs_) +// { +// return false; +// } +// inputIds_[outputNumber] = opId; +// return true; +// } + +void nt::GeometryOperator::addInputConnection(std::shared_ptr connection) { - if(inputNumber>=maxInputs_) - { - return false; - } - inputIds_[inputNumber] = opId; - - return true; + inputConnections_.push_back(connection); } -bool nt::GeometryOperator::setOutput(unsigned int outputNumber, nt::OpId opId) + +void nt::GeometryOperator::addOutputConnection(std::shared_ptr connection) { - - if(outputNumber>=maxOutputs_) - { - return false; - } - inputIds_[outputNumber] = opId; - return true; + std::cout << "Output connection added\nConnecting ops " << connection->getInputOpId() << " -> " << connection->getOutputOpId() << "\n"; + std::cout << "Connecting index " << connection->getInputIndex() << " -> " << connection->getOutputIndex() << "\n"; + outputConnections_.push_back(connection); } -std::optional nt::GeometryOperator::getInput(unsigned int inputNumber) const + +// std::optional nt::GeometryOperator::getInput(unsigned int inputNumber) const +// { +// if(inputNumber>=maxInputs_) +// { +// return std::nullopt; +// } +// return inputIds_.at(inputNumber); +// } + +// std::optional nt::GeometryOperator::getOutput(unsigned int outputNumber) const +// { +// if(outputNumber>=maxOutputs_) +// { +// return std::nullopt; +// } +// return outputIds_.at(outputNumber); +// } + + +unsigned int nt::GeometryOperator::getMaxInputs() const { - if(inputNumber>=maxInputs_) - { - return std::nullopt; - } - return inputIds_.at(inputNumber); + return maxInputs_; } - -std::optional nt::GeometryOperator::getOutput(unsigned int outputNumber) const +unsigned int nt::GeometryOperator::getMaxOutputs() const { - if(outputNumber>=maxOutputs_) - { - return std::nullopt; - } - return outputIds_.at(outputNumber); + return maxOutputs_; } - - diff --git a/src/Engine/Operator/GeometryOperator.h b/src/Engine/Operator/GeometryOperator.h index 9160ead..5d5ff39 100644 --- a/src/Engine/Operator/GeometryOperator.h +++ b/src/Engine/Operator/GeometryOperator.h @@ -1,21 +1,34 @@ #pragma once +#include "Engine/Operator/GeometryConnection.h" #include "Engine/Types.h" #include +#include namespace enzo::nt { +void connectOperators(enzo::nt::OpId inputOpId, unsigned int inputIndex, enzo::nt::OpId outputOpId, unsigned int outputIndex); class GeometryOperator { public: GeometryOperator(); - bool setInput(unsigned int inputNumber, nt::OpId opId); - bool setOutput(unsigned int outputNumber, nt::OpId opId); - std::optional getInput(unsigned int inputNumber) const; - std::optional getOutput(unsigned int outputNumber) const; + // bool setInput(unsigned int inputNumber, nt::OpId opId); + // bool setOutput(unsigned int outputNumber, nt::OpId opId); + // std::optional getInput(unsigned int inputNumber) const; + // std::optional getOutput(unsigned int outputNumber) const; + + void addInputConnection(std::shared_ptr connection); + void addOutputConnection(std::shared_ptr connection); + + + + unsigned int getMaxInputs() const; + unsigned int getMaxOutputs() const; + private: - std::vector> inputIds_; - std::vector> outputIds_; + // TODO: avoid duplicate connections + std::vector> inputConnections_; + std::vector> outputConnections_; unsigned int maxInputs_; unsigned int maxOutputs_; diff --git a/src/Engine/Types.h b/src/Engine/Types.h index 1d3a83b..84ff885 100644 --- a/src/Engine/Types.h +++ b/src/Engine/Types.h @@ -32,5 +32,10 @@ namespace enzo namespace nt { using OpId = uint64_t; + + enum class SocketIOType { + Input, + Output + }; } } diff --git a/src/gui/network/FloatingEdgeGraphic.cpp b/src/gui/network/FloatingEdgeGraphic.cpp index 3f8ea11..3f85983 100644 --- a/src/gui/network/FloatingEdgeGraphic.cpp +++ b/src/gui/network/FloatingEdgeGraphic.cpp @@ -32,8 +32,8 @@ void FloatingEdgeGraphic::paint(QPainter *painter, const QStyleOptionGraphicsIte painter->setPen(pen); // painter->drawLine(socket1_->scenePos(),floatPos_); - QPointF pos1 = socket1_->getIO()==SocketGraphic::SocketType::Input ? socket1_->scenePos() : floatPos_; - QPointF pos2 = socket1_->getIO()==SocketGraphic::SocketType::Input ? floatPos_ : socket1_->scenePos(); + QPointF pos1 = socket1_->getIO()==enzo::nt::SocketIOType::Input ? socket1_->scenePos() : floatPos_; + QPointF pos2 = socket1_->getIO()==enzo::nt::SocketIOType::Input ? floatPos_ : socket1_->scenePos(); float dist = std::sqrt(std::pow(pos1.x()-pos2.x(),2)+std::pow(pos1.y()-pos2.y(),2)); std::cout << "dist: " << dist << "\n"; float cubicStrength = dist*0.5; diff --git a/src/gui/network/Network.cpp b/src/gui/network/Network.cpp index 9ae835b..12def1d 100644 --- a/src/gui/network/Network.cpp +++ b/src/gui/network/Network.cpp @@ -1,4 +1,6 @@ #include "gui/network/Network.h" +#include "Engine/Operator/GeometryConnection.h" +#include "Engine/Operator/GeometryOperator.h" #include "Engine/Types.h" #include "gui/network/DisplayFlagButton.h" #include "gui/network/NodeEdgeGraphic.h" @@ -8,6 +10,7 @@ #include "gui/network/FloatingEdgeGraphic.h" #include "gui/network/SocketGraphic.h" #include "Engine/Network/NetworkManager.h" +#include #include #include #include @@ -126,14 +129,27 @@ void Network::socketClicked(SocketGraphic* socket, QMouseEvent *event) } // second click // connect to opposite type - else if (socket->getIO()!=startSocket_->getIO()) + else if ( + socket->getIO()!=startSocket_->getIO() && + startSocket_->getOpId()!=socket->getOpId() + ) { - auto inputSocket = startSocket_->getIO()==SocketGraphic::SocketType::Input ? startSocket_ : socket; - auto outputSocket = socket->getIO()==SocketGraphic::SocketType::Output ? socket : startSocket_; - NodeEdgeGraphic* newEdge = new NodeEdgeGraphic(inputSocket, outputSocket); + // order sockets in relation to data flow + // the input node is the node the data flows from + auto inputNodeSocket = socket->getIO()==enzo::nt::SocketIOType::Output ? socket : startSocket_; + // the output node is the node the data flows to + auto outputNodeSocket = startSocket_->getIO()==enzo::nt::SocketIOType::Input ? startSocket_ : socket; - newEdge->setPos(inputSocket->scenePos(), outputSocket->scenePos()); + std::cout << "CONNECTING opid: " << inputNodeSocket->getOpId() << " -> " << outputNodeSocket->getOpId() << "\n"; + + + nt::connectOperators(inputNodeSocket->getOpId(), inputNodeSocket->getIndex(), outputNodeSocket->getOpId(), outputNodeSocket->getIndex()); + + + NodeEdgeGraphic* newEdge = new NodeEdgeGraphic(outputNodeSocket, inputNodeSocket); + + newEdge->setPos(outputNodeSocket->scenePos(), inputNodeSocket->scenePos()); scene_->addItem(newEdge); destroyFloatingEdge(); } diff --git a/src/gui/network/NodeGraphic.cpp b/src/gui/network/NodeGraphic.cpp index 75851d9..70bc5d9 100644 --- a/src/gui/network/NodeGraphic.cpp +++ b/src/gui/network/NodeGraphic.cpp @@ -5,6 +5,8 @@ #include #include #include +#include "Engine/Network/NetworkManager.h" +#include "Engine/Operator/GeometryOperator.h" #include "gui/network/DisplayFlagButton.h" #include "gui/network/SocketGraphic.h" #include @@ -76,13 +78,22 @@ void NodeGraphic::initFlagButtons() void NodeGraphic::initSockets() { - auto* socketInput = new SocketGraphic(SocketGraphic::SocketType::Input, this); - socketInput->setPos(getSocketPosition(0, SocketGraphic::SocketType::Input)); - inputs_.push_back(socketInput); + auto nm = enzo::nt::NetworkManager::getInstance(); + enzo::nt::GeometryOperator& op = nm->getGeoOperator(opId_); + for(int i=0, max=op.getMaxInputs(); isetPos(getSocketPosition(i, enzo::nt::SocketIOType::Input)); + inputs_.push_back(socketInput); + } + + for(int i=0, max=op.getMaxOutputs(); isetPos(getSocketPosition(i, enzo::nt::SocketIOType::Output)); + outputs_.push_back(socketOutput); + } - auto* socketOutput = new SocketGraphic(SocketGraphic::SocketType::Output, this); - socketOutput->setPos(getSocketPosition(0, SocketGraphic::SocketType::Output)); - outputs_.push_back(socketOutput); } // void setInputEdge(NodeEdgeGraphic* edge, int indx) @@ -183,23 +194,30 @@ void NodeGraphic::updatePositions(QPointF pos) // but for now I'm just going based on order for(int socketIndex=0; socketIndexposChanged(getSocketScenePosition(socketIndex, SocketGraphic::SocketType::Input)); + inputs_[socketIndex]->posChanged(getSocketScenePosition(socketIndex, enzo::nt::SocketIOType::Input)); } for(int socketIndex=0; socketIndexposChanged(getSocketScenePosition(socketIndex, SocketGraphic::SocketType::Output)); + outputs_[socketIndex]->posChanged(getSocketScenePosition(socketIndex, enzo::nt::SocketIOType::Output)); } } -QPointF NodeGraphic::getSocketPosition(int socketIndex, SocketGraphic::SocketType socketType) +QPointF NodeGraphic::getSocketPosition(int socketIndex, enzo::nt::SocketIOType socketType) { + auto nm = enzo::nt::NetworkManager::getInstance(); + enzo::nt::GeometryOperator& op = nm->getGeoOperator(opId_); + int maxSocketNumber = socketType==enzo::nt::SocketIOType::Input ? op.getMaxInputs() : op.getMaxOutputs(); + float socketSpread = socketSize_*1.5*maxSocketNumber; + float xPos, yPos; xPos = bodyRect_.center().x(); - yPos = socketType == SocketGraphic::SocketType::Input ? bodyRect_.top() : bodyRect_.bottom(); + yPos = socketType == enzo::nt::SocketIOType::Input ? bodyRect_.top() : bodyRect_.bottom(); + + xPos += ((socketIndex/static_cast(maxSocketNumber-1))-0.5)*2*socketSpread; return QPointF(xPos, yPos); } -QPointF NodeGraphic::getSocketScenePosition(int socketIndex, SocketGraphic::SocketType socketType) +QPointF NodeGraphic::getSocketScenePosition(int socketIndex, enzo::nt::SocketIOType socketType) { return this->pos()+getSocketPosition(socketIndex, socketType); } diff --git a/src/gui/network/NodeGraphic.h b/src/gui/network/NodeGraphic.h index f48b0d2..5def34f 100644 --- a/src/gui/network/NodeGraphic.h +++ b/src/gui/network/NodeGraphic.h @@ -17,8 +17,8 @@ public: SocketGraphic* getInput(int indx) const; SocketGraphic* getOutput(int indx) const; - QPointF getSocketPosition(int socketIndex, SocketGraphic::SocketType socketType); - QPointF getSocketScenePosition(int socketIndex, SocketGraphic::SocketType socketType); + QPointF getSocketPosition(int socketIndex, enzo::nt::SocketIOType socketType); + QPointF getSocketScenePosition(int socketIndex, enzo::nt::SocketIOType socketType); QRectF getBodyRect(); // void addEdge(NodeEdgeGraphic* edge); diff --git a/src/gui/network/SocketGraphic.cpp b/src/gui/network/SocketGraphic.cpp index 273a10f..ec6f616 100644 --- a/src/gui/network/SocketGraphic.cpp +++ b/src/gui/network/SocketGraphic.cpp @@ -4,8 +4,8 @@ #include #include "gui/network/NodeEdgeGraphic.h" -SocketGraphic::SocketGraphic(SocketGraphic::SocketType type, QGraphicsItem *parent) -: QGraphicsItem(parent), type_{type} +SocketGraphic::SocketGraphic(enzo::nt::SocketIOType type, enzo::nt::OpId opId, unsigned int socketIndex, QGraphicsItem *parent) +: QGraphicsItem(parent), type_{type}, opId_{opId}, socketIndex_{socketIndex} { brushActive_ = QBrush("white"); brushInactive_ = QBrush("#9f9f9f"); @@ -14,6 +14,12 @@ SocketGraphic::SocketGraphic(SocketGraphic::SocketType type, QGraphicsItem *pare initBoundingBox(); } +unsigned int SocketGraphic::getIndex() const +{ + return socketIndex_; +} + + void SocketGraphic::addEdge(NodeEdgeGraphic* edge) { std::cout << "adding edge\n"; @@ -51,11 +57,11 @@ void SocketGraphic::posChanged(QPointF pos) for(auto* edge : edges_) { // edge->setPos(startSocket_->scenePos(), socket->scenePos()); - if(type_==SocketType::Input) + if(type_==enzo::nt::SocketIOType::Input) { edge->setStartPos(pos); } - else if(type_==SocketType::Output) + else if(type_==enzo::nt::SocketIOType::Output) { edge->setEndPos(pos); } @@ -63,6 +69,10 @@ void SocketGraphic::posChanged(QPointF pos) } +enzo::nt::OpId SocketGraphic::getOpId() const +{ + return opId_; +} QRectF SocketGraphic::boundingRect() const { @@ -71,16 +81,16 @@ QRectF SocketGraphic::boundingRect() const QPainterPath SocketGraphic::shape() const{ QPainterPath path; - QPointF startPt(boundRect_.center().x(), type_==SocketType::Input ? boundRect_.top() : boundRect_.bottom()); + QPointF startPt(boundRect_.center().x(), type_==enzo::nt::SocketIOType::Input ? boundRect_.top() : boundRect_.bottom()); path.moveTo(startPt); - path.arcTo(boundRect_, 0, type_==SocketType::Input ? 180 : -180); + path.arcTo(boundRect_, 0, type_==enzo::nt::SocketIOType::Input ? 180 : -180); path.lineTo(boundRect_.right(), boundRect_.center().y()); path.closeSubpath(); return path; } -SocketGraphic::SocketType SocketGraphic::getIO() { return type_; } +enzo::nt::SocketIOType SocketGraphic::getIO() { return type_; } void SocketGraphic::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) diff --git a/src/gui/network/SocketGraphic.h b/src/gui/network/SocketGraphic.h index 6f9db1c..7458c8e 100644 --- a/src/gui/network/SocketGraphic.h +++ b/src/gui/network/SocketGraphic.h @@ -1,4 +1,5 @@ #pragma once +#include "Engine/Types.h" #include #include #include @@ -14,27 +15,28 @@ public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - enum class SocketType { - Input, - Output - }; - SocketGraphic(SocketGraphic::SocketType type, QGraphicsItem *parent = nullptr); - SocketType getIO(); + SocketGraphic(enzo::nt::SocketIOType type, enzo::nt::OpId opId, unsigned int socketIndex, QGraphicsItem *parent = nullptr); + enzo::nt::SocketIOType getIO(); void addEdge(NodeEdgeGraphic* edge); void removeEdge(NodeEdgeGraphic* edge); void posChanged(QPointF pos); QPainterPath shape() const override; + enzo::nt::OpId getOpId() const; + + unsigned int getIndex() const; private: int socketSize_ = 1; + unsigned int socketIndex_; QBrush brushInactive_; QBrush brushActive_; bool hovered_=false; - SocketType type_; + enzo::nt::SocketIOType type_; std::unordered_set edges_; qreal paddingScale_=20; QRectF boundRect_; + enzo::nt::OpId opId_; void initBoundingBox(); protected: diff --git a/tests/NetworkTests.cpp b/tests/NetworkTests.cpp index db32dac..deb6f52 100644 --- a/tests/NetworkTests.cpp +++ b/tests/NetworkTests.cpp @@ -1,4 +1,5 @@ #include +#include #include "Engine/Network/NetworkManager.h" #include "Engine/Operator/GeometryOperator.h" #include "Engine/Types.h" @@ -14,12 +15,17 @@ TEST_CASE("network") REQUIRE(nm->isValidOp(newOpId)); if(nm->isValidOp(newOpId)) { - nt::GeometryOperator& newOp = nm->getGeoOperator(newOpId); - REQUIRE(newOp.setInput(0, newOpId2)); + auto newConnection = std::make_shared(newOpId, 1, newOpId2, 3); + + auto inputOp = nm->getGeoOperator(newOpId); + auto outputOp = nm->getGeoOperator(newOpId2); + + // set output on the upper operator + outputOp.addOutputConnection(newConnection); + + // set input on the lower operator + inputOp.addInputConnection(newConnection); - std::optional returnOpId = newOp.getInput(0); - REQUIRE(returnOpId.has_value()); - REQUIRE(*returnOpId==newOpId2); } }