diff --git a/apps/sample-gui/CMakeLists.txt b/apps/sample-gui/CMakeLists.txt index 7190117..8692893 100644 --- a/apps/sample-gui/CMakeLists.txt +++ b/apps/sample-gui/CMakeLists.txt @@ -7,6 +7,8 @@ list(APPEND client_HEADERS audio_editor/AudioEditorView.h image_editor/ImageEditorView.h image_editor/ImageViewWidget.h + canvas/CanvasView.h + canvas/CanvasController.h web_client/WebClientView.h) @@ -18,6 +20,8 @@ list(APPEND client_LIB_INCLUDES audio_editor/AudioEditorView.cpp image_editor/ImageEditorView.cpp image_editor/ImageViewWidget.cpp + canvas/CanvasView.cpp + canvas/CanvasController.cpp web_client/WebClientView.cpp MediaTool.cpp) @@ -37,6 +41,7 @@ target_include_directories(sample_gui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/audio_editor" "${CMAKE_CURRENT_SOURCE_DIR}/image_editor" "${CMAKE_CURRENT_SOURCE_DIR}/web_client" + "${CMAKE_CURRENT_SOURCE_DIR}/canvas" ) target_link_libraries(sample_gui PUBLIC client windows console core network database geometry audio web) set_property(TARGET sample_gui PROPERTY FOLDER apps) diff --git a/apps/sample-gui/MediaTool.cpp b/apps/sample-gui/MediaTool.cpp index 2f83e4c..7f377a5 100644 --- a/apps/sample-gui/MediaTool.cpp +++ b/apps/sample-gui/MediaTool.cpp @@ -4,6 +4,8 @@ #include "AudioEditorView.h" #include "ImageEditorView.h" #include "WebClientView.h" +#include "CanvasView.h" + #include "TabbedPanelWidget.h" #include "TopBar.h" #include "TextNode.h" @@ -31,9 +33,9 @@ void MediaTool::initializeViews() auto path = mMainApplication->getCommandLineArgs()->getLaunchPath(); path /= "out.txt"; textEditor->setName("TextEditor"); - textEditor->GetController()->SetSavePath(path); - textEditor->GetController()->SetLoadPath(path); - textEditor->Initialize(); + textEditor->getController()->SetSavePath(path); + textEditor->getController()->SetLoadPath(path); + textEditor->initialize(); tabbedPanel->addPanel(std::move(textEditor), "Text Editor"); auto audioEditor = AudioEditorView::Create(); @@ -48,6 +50,10 @@ void MediaTool::initializeViews() webClient->setName("webClient"); tabbedPanel->addPanel(std::move(webClient), "Web Client"); + auto canvas = CanvasView::Create(); + canvas->setName("CanvasView"); + tabbedPanel->addPanel(std::move(canvas), "Canvas"); + auto topBar = TopBar::Create(); auto statusBar = StatusBar::Create(); diff --git a/apps/sample-gui/canvas/CanvasController.cpp b/apps/sample-gui/canvas/CanvasController.cpp new file mode 100644 index 0000000..83829d3 --- /dev/null +++ b/apps/sample-gui/canvas/CanvasController.cpp @@ -0,0 +1,6 @@ +#include "CanvasController.h" + +std::unique_ptr CanvasController::Create() +{ + return std::make_unique(); +} diff --git a/apps/sample-gui/canvas/CanvasController.h b/apps/sample-gui/canvas/CanvasController.h new file mode 100644 index 0000000..9d56ec5 --- /dev/null +++ b/apps/sample-gui/canvas/CanvasController.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +class CanvasController +{ +public: + static std::unique_ptr Create(); +}; diff --git a/apps/sample-gui/canvas/CanvasView.cpp b/apps/sample-gui/canvas/CanvasView.cpp new file mode 100644 index 0000000..c8fbc6e --- /dev/null +++ b/apps/sample-gui/canvas/CanvasView.cpp @@ -0,0 +1,75 @@ +#include "CanvasView.h" + +#include "HorizontalSpacer.h" +#include "VerticalSpacer.h" + +#include "CanvasController.h" + +#include "Theme.h" + +#include "TextNode.h" +#include "Label.h" +#include "Button.h" + +#include + +CanvasView::CanvasView() + : mController(CanvasController::Create()) +{ + initialize(); +} + +CanvasView::~CanvasView() +{ + +} + +std::unique_ptr CanvasView::Create() +{ + return std::make_unique(); +} + +void CanvasView::initialize() +{ + auto label = Label::Create(); + label->setLabel("Canvas"); + label->setBackgroundColor(Theme::getBannerBackground()); + label->setMargin(1); + + + + auto hSpacer = HorizontalSpacer::Create(); + hSpacer->addWidgetWithScale(std::move(label), 1); + //hSpacer->addWidgetWithScale(std::move(textBox), 14); + + auto cache_button_spacer = initializeCacheButtons(); + hSpacer->addWidgetWithScale(std::move(cache_button_spacer), 1); + + addWidget(std::move(hSpacer)); +} + +std::unique_ptr CanvasView::initializeCacheButtons() +{ + auto saveButton = Button::Create(); + saveButton->setLabel("Save"); + saveButton->setBackgroundColor(Theme::getButtonPrimaryBackground()); + saveButton->setMargin(2); + + auto clearButton = Button::Create(); + clearButton->setLabel("Clear"); + clearButton->setBackgroundColor(Theme::getButtonPrimaryBackground()); + clearButton->setMargin(2); + + auto loadButton = Button::Create(); + loadButton->setLabel("Load"); + loadButton->setBackgroundColor(Theme::getButtonPrimaryBackground()); + loadButton->setMargin(2); + + auto buttonSpacer = VerticalSpacer::Create(); + buttonSpacer->AddWidgetWithScale(std::move(saveButton), 1); + buttonSpacer->AddWidgetWithScale(std::move(clearButton), 1); + buttonSpacer->AddWidgetWithScale(std::move(loadButton), 1); + + return buttonSpacer; +} + diff --git a/apps/sample-gui/canvas/CanvasView.h b/apps/sample-gui/canvas/CanvasView.h new file mode 100644 index 0000000..ddc4c3e --- /dev/null +++ b/apps/sample-gui/canvas/CanvasView.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Widget.h" + +class CanvasController; + +class CanvasView : public Widget +{ +public: + CanvasView(); + + ~CanvasView(); + + static std::unique_ptr Create(); + +private: + + void initialize(); + + std::unique_ptr initializeCacheButtons(); + + //std::unique_ptr initializeCacheButtons(); + + std::unique_ptr mController; +}; +using CanvasViewPtr = std::unique_ptr; diff --git a/apps/sample-gui/text_editor/TextEditorController.h b/apps/sample-gui/text_editor/TextEditorController.h index 2a62067..37d717d 100644 --- a/apps/sample-gui/text_editor/TextEditorController.h +++ b/apps/sample-gui/text_editor/TextEditorController.h @@ -5,10 +5,6 @@ class TextEditorController { - TextEditorModelUPtr mModel; - std::filesystem::path mSavePath; - std::filesystem::path mLoadPath; - public: TextEditorController(); @@ -28,6 +24,11 @@ public: void SetSavePath(const std::filesystem::path& path); void SetLoadPath(const std::filesystem::path& path); + +private: + TextEditorModelUPtr mModel; + std::filesystem::path mSavePath; + std::filesystem::path mLoadPath; }; using TextEditorControllerUPtr = std::unique_ptr; diff --git a/apps/sample-gui/text_editor/TextEditorView.cpp b/apps/sample-gui/text_editor/TextEditorView.cpp index b557819..26e322c 100644 --- a/apps/sample-gui/text_editor/TextEditorView.cpp +++ b/apps/sample-gui/text_editor/TextEditorView.cpp @@ -16,12 +16,12 @@ TextEditorView::TextEditorView() } -TextEditorController* TextEditorView::GetController() +TextEditorController* TextEditorView::getController() { return mController.get(); } -void TextEditorView::Initialize() +void TextEditorView::initialize() { auto label = Label::Create(); label->setLabel("Text Editor"); diff --git a/apps/sample-gui/text_editor/TextEditorView.h b/apps/sample-gui/text_editor/TextEditorView.h index 68d80ae..57aae3d 100644 --- a/apps/sample-gui/text_editor/TextEditorView.h +++ b/apps/sample-gui/text_editor/TextEditorView.h @@ -5,21 +5,23 @@ #include "Label.h" #include "TextBox.h" #include "TextEditorController.h" + #include class TextEditorView : public Widget { - TextBox* mTextBox; - TextEditorControllerUPtr mController; - public: TextEditorView(); static std::unique_ptr Create(); - TextEditorController* GetController(); + TextEditorController* getController(); - void Initialize(); + void initialize(); + +private: + TextBox* mTextBox; + TextEditorControllerUPtr mController; }; using TextEditorViewUPtr = std::unique_ptr; diff --git a/src/mesh/AbstractFace.cpp b/src/mesh/AbstractFace.cpp index c41a394..c3f51c4 100644 --- a/src/mesh/AbstractFace.cpp +++ b/src/mesh/AbstractFace.cpp @@ -1,11 +1,18 @@ #include "AbstractFace.h" +#include "Edge.h" + AbstractFace::AbstractFace(unsigned id) : mId(id) { } +AbstractFace::~AbstractFace() +{ + +} + void AbstractFace::addVectorAttribute(const std::string& tag, const std::vector& values) { mVectorAttributes[tag] = values; @@ -20,3 +27,8 @@ std::vector AbstractFace::getVectorAttribute(const std::string& tag) con } return {}; } + +void AbstractFace::setIndex(std::size_t idx) +{ + mId = idx; +} diff --git a/src/mesh/AbstractFace.h b/src/mesh/AbstractFace.h index 5c4649d..511d457 100644 --- a/src/mesh/AbstractFace.h +++ b/src/mesh/AbstractFace.h @@ -5,12 +5,14 @@ #include #include +class Edge; + class AbstractFace { public: AbstractFace(unsigned id=0); - virtual ~AbstractFace() = default; + virtual ~AbstractFace(); virtual std::vector getNodeIds() const = 0; @@ -18,6 +20,16 @@ public: std::vector getVectorAttribute(const std::string& tag) const; + void setIndex(std::size_t idx); + + virtual unsigned getNumNodes() const = 0; + + virtual void associateWidthEdges() = 0; + + virtual void replaceEdge(Edge* original, Edge* replacement) = 0; + + virtual std::vector getEdgeIds() const = 0; + protected: unsigned mId{0}; std::unordered_map > mVectorAttributes; diff --git a/src/mesh/AbstractMesh.h b/src/mesh/AbstractMesh.h index 6acfe90..975f55d 100644 --- a/src/mesh/AbstractMesh.h +++ b/src/mesh/AbstractMesh.h @@ -40,6 +40,8 @@ public: return ret; } + virtual std::unique_ptr copy() const = 0; + std::vector > getNodeVectorAttributes(const std::string& tag); std::vector getVectorAttribute(const std::string& tag) const; diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index e1b239b..f654d47 100644 --- a/src/mesh/CMakeLists.txt +++ b/src/mesh/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND mesh_LIB_INCLUDES Node.cpp QuadMesh.cpp TriMesh.cpp + FaceMesh.cpp LineMesh.cpp MeshPrimitives.cpp MeshBuilder.cpp) diff --git a/src/mesh/Edge.cpp b/src/mesh/Edge.cpp index 35d8dec..5fc6137 100644 --- a/src/mesh/Edge.cpp +++ b/src/mesh/Edge.cpp @@ -28,3 +28,95 @@ unsigned Edge::getNode1Id() const { return mNode1->getIndex(); } + +bool Edge::isOverlapping(Edge* edge) const +{ + auto overlaps = edge->getNode0()->isCoincident(mNode0) && edge->getNode1()->isCoincident(mNode1); + if (overlaps) + { + return true; + } + else + { + return edge->getNode1()->isCoincident(mNode0) && edge->getNode0()->isCoincident(mNode1); + } +} + +bool Edge::hasSameNodes(Edge* edge) const +{ + return edge->getNode0() == edge->getNode1() || edge->getNode1() == edge->getNode0(); +} + +void Edge::replaceNodes(Node* node0, Node* node1) +{ + mNode0 = node0; + mNode1 = node1; +} + +Node* Edge::getNode0() const +{ + return mNode0; +} + +Node* Edge::getNode1() const +{ + return mNode1; +} + +Node* Edge::getOtherNode(Node* node) const +{ + if (node == mNode0) + { + return mNode1; + } + else + { + return mNode0; + } +} + +unsigned Edge::getId() const +{ + return mId; +} + +void Edge::setState(State state) +{ + mState = state; +} + +Edge::State Edge::getState() const +{ + return mState; +} + +void Edge::clearConnectivity() +{ + mAssociatedFaceIds.clear(); +} + +unsigned Edge::getNumConnectedFaces() const +{ + return mAssociatedFaceIds.size(); +} + +void Edge::associateFace(unsigned faceId) +{ + mAssociatedFaceIds.push_back(faceId); +} + +unsigned Edge::getConnectedFaceId(std::size_t idx) const +{ + return mAssociatedFaceIds[idx]; +} + +void Edge::associateWithNodes() +{ + mNode0->associateEdge(mId); + mNode1->associateEdge(mId); +} + +void Edge::setIndex(unsigned idx) +{ + mId = idx; +} diff --git a/src/mesh/Edge.h b/src/mesh/Edge.h index 6e73af9..83d7d9d 100644 --- a/src/mesh/Edge.h +++ b/src/mesh/Edge.h @@ -1,23 +1,65 @@ #pragma once -class Node; - #include +#include + +class Node; class Edge { public: + enum class State + { + HEALTHY, + DIRTY + }; + Edge(Node* node0, Node* node1, unsigned id = 0); + ~Edge(); static std::unique_ptr Create(Node* node0, Node* node1, unsigned id = 0); + void associateFace(unsigned faceId); + + void associateWithNodes(); + + void clearConnectivity(); + + unsigned getConnectedFaceId(std::size_t idx) const; + + State getState() const; + + unsigned getId() const; + unsigned getNode0Id() const; unsigned getNode1Id() const; + Node* getNode0() const; + + Node* getNode1() const; + + unsigned getNumConnectedFaces() const; + + Node* getOtherNode(Node* node) const; + + bool isOverlapping(Edge* edge) const; + + bool hasSameNodes(Edge* edge) const; + + void replaceNodes(Node* node0, Node* node1); + + void setState(State state); + + void setIndex(unsigned idx); + private: unsigned mId{0}; Node* mNode0{nullptr}; Node* mNode1{nullptr}; + + std::vector mAssociatedFaceIds; + + State mState{State::HEALTHY}; }; diff --git a/src/mesh/FaceMesh.cpp b/src/mesh/FaceMesh.cpp new file mode 100644 index 0000000..b08b77e --- /dev/null +++ b/src/mesh/FaceMesh.cpp @@ -0,0 +1,182 @@ +#include "FaceMesh.h" + +#include "Node.h" +#include "Edge.h" +#include "AbstractFace.h" + +FaceMesh::~FaceMesh() +{ + +} + +void FaceMesh::populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces) +{ + mNodes = std::move(nodes); + mEdges = std::move(edges); + mFaces = std::move(faces); + + resetIds(); + resetConnectivity(); +} + +std::vector FaceMesh::getFaceNodeIds() const +{ + if (mFaces.empty()) + { + return {}; + } + + auto nodes_per_face = mFaces[0]->getNumNodes(); + std::vector ids(nodes_per_face*mFaces.size()); + + for(std::size_t idx=0; idxgetNodeIds(); + for(std::size_t jdx=0; jdx > FaceMesh::getFaceVectorAttributes(const std::string& tag) +{ + std::vector > attribs(mFaces.size()); + for(std::size_t idx=0; idxgetVectorAttribute(tag)}; + } + return attribs; +} + +void FaceMesh::addConstantFaceVectorAttribute(const std::string& tag, const std::vector& values) +{ + for (const auto& face : mFaces) + { + face->addVectorAttribute(tag, values); + } +} + +void FaceMesh::resetIds() +{ + unsigned count = 0; + for (auto& node : mNodes) + { + node->setIndex(count); + } + + count = 0; + for (auto& edge : mEdges) + { + edge->setIndex(count); + } + + count = 0; + for (auto& face : mFaces) + { + face->setIndex(count); + } +} + +void FaceMesh::resetConnectivity() +{ + for (auto& node : mNodes) + { + node->clearConnectivity(); + } + + for (auto& edge : mEdges) + { + edge->associateWithNodes(); + edge->clearConnectivity(); + } + + for (auto& face : mFaces) + { + face->associateWidthEdges(); + } +} + +void FaceMesh::replaceIfOverlapping(FaceMesh* mesh, Node* target_node) const +{ + for (auto& node : mNodes) + { + if (node->getNumConnectedEdges() == 3 && node->isCoincident(target_node)) + { + target_node->setState(Node::State::DIRTY); + for (unsigned idx=0; idx<3; idx++) + { + auto edge = mesh->mEdges[idx].get(); + edge->replaceNodes(target_node, node.get()); + } + break; + } + } +} + +void FaceMesh::replaceIfOverlapping(FaceMesh* mesh, Edge* target_edge) const +{ + for (auto& edge : mEdges) + { + if (edge->getNumConnectedFaces() == 2 && target_edge->hasSameNodes(edge.get())) + { + target_edge->setState(Edge::State::DIRTY); + for (unsigned idx=0; idx<2; idx++) + { + auto face = mesh->mFaces[idx].get(); + face->replaceEdge(target_edge, edge.get()); + } + break; + } + } +} + +void FaceMesh::merge(std::unique_ptr mesh) +{ + if (mesh->getType() != getType()) + { + return; + } + + for (auto& node : mesh->mNodes) + { + if (node->getNumConnectedEdges() == 3) + { + replaceIfOverlapping(mesh.get(), node.get()); + } + } + + for (auto& node : mesh->mNodes) + { + if (node->getState() == Node::State::HEALTHY) + { + mNodes.push_back(std::move(node)); + } + } + + for (auto& edge : mesh->mEdges) + { + if (edge->getNumConnectedFaces() == 2) + { + replaceIfOverlapping(mesh.get(), edge.get()); + } + } + + for (auto& edge : mesh->mEdges) + { + if (edge->getState() == Edge::State::HEALTHY) + { + mEdges.push_back(std::move(edge)); + } + } + + for (auto& face : mesh->mFaces) + { + mFaces.push_back(std::move(face)); + } + + mesh.reset(); + resetIds(); + resetConnectivity(); +} diff --git a/src/mesh/FaceMesh.h b/src/mesh/FaceMesh.h new file mode 100644 index 0000000..2596115 --- /dev/null +++ b/src/mesh/FaceMesh.h @@ -0,0 +1,47 @@ +#pragma once + +#include "AbstractMesh.h" + +#include +#include + +class Edge; +class AbstractFace; + +using EdgePtr = std::unique_ptr; +using AbstractFacePtr = std::unique_ptr; + +using VecEdges = std::vector; +using VecFaces = std::vector; + +class FaceMesh : public AbstractMesh +{ +public: + FaceMesh() = default; + + ~FaceMesh(); + + virtual std::unique_ptr copy() const = 0; + + void populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces); + + std::vector getFaceNodeIds() const; + + void addConstantFaceVectorAttribute(const std::string& tag, const std::vector& values); + + std::vector > getFaceVectorAttributes(const std::string& tag); + + void merge(std::unique_ptr mesh); + +protected: + void resetConnectivity(); + + void resetIds(); + + void replaceIfOverlapping(FaceMesh* mesh, Node* node) const; + + void replaceIfOverlapping(FaceMesh* mesh, Edge* edge) const; + + VecEdges mEdges; + VecFaces mFaces; +}; diff --git a/src/mesh/LineMesh.cpp b/src/mesh/LineMesh.cpp index 3823244..4a864de 100644 --- a/src/mesh/LineMesh.cpp +++ b/src/mesh/LineMesh.cpp @@ -13,6 +13,16 @@ void LineMesh::populate(VecNodes& nodes, VecEdges& edges) mEdges = std::move(edges); } +std::unique_ptr LineMesh::copy() const +{ + return nullptr; +} + +LineMesh::MeshType LineMesh::getType() const +{ + return LineMesh::MeshType::LINE; +} + std::vector LineMesh::getEdgeNodeIds() const { std::vector ids(2*mEdges.size()); diff --git a/src/mesh/LineMesh.h b/src/mesh/LineMesh.h index 0116036..3cf98ad 100644 --- a/src/mesh/LineMesh.h +++ b/src/mesh/LineMesh.h @@ -20,10 +20,9 @@ public: std::vector getEdgeNodeIds() const; - MeshType getType() const override - { - return MeshType::LINE; - } + MeshType getType() const override; + + std::unique_ptr copy() const override; private: VecEdges mEdges; diff --git a/src/mesh/MeshPrimitives.cpp b/src/mesh/MeshPrimitives.cpp index 066d3ae..38308ca 100644 --- a/src/mesh/MeshPrimitives.cpp +++ b/src/mesh/MeshPrimitives.cpp @@ -2,6 +2,8 @@ #include "MeshBuilder.h" +#include + #include std::unique_ptr MeshPrimitives::buildRectangleAsTriMesh() @@ -29,6 +31,85 @@ std::unique_ptr MeshPrimitives::buildRectangleAsTriMesh() return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids); } +std::unique_ptr MeshPrimitives::buildRoundedRectangleAsTriMesh(double radius, double aspect_ratio, unsigned num_segments) +{ + unsigned num_fans = 4; + unsigned num_nodes_per_fan = num_segments + 2; + VecPoints locations(num_fans * num_nodes_per_fan); + + double rect_start_x = radius; + double rect_end_x = 1.0 - radius; + double rect_end_y = 1.0 - radius; + + double delta_theta = (M_PI/4.0) / double (num_segments); + double running_theta = 0; + + locations[0] = {rect_end_x, radius}; + unsigned offset = 1; + for (unsigned idx=0; idx<=num_segments; idx++) + { + locations[offset + idx] = {rect_end_x + radius*sin(running_theta), radius*(1.0 - cos(running_theta))}; + running_theta += delta_theta; + } + offset += num_segments; + + locations[offset] = {rect_end_x, rect_end_y}; + offset++; + running_theta = 0; + for (unsigned idx=0; idx<=num_segments;idx++) + { + locations[offset + idx] = {rect_end_x + radius*cos(running_theta), rect_end_y + radius*sin(running_theta)}; + running_theta += delta_theta; + } + offset += num_segments; + + locations[offset] = {rect_start_x, rect_end_y}; + offset ++; + running_theta = 0; + for (unsigned idx=0; idx<=num_segments;idx++) + { + locations[offset + idx] = {rect_start_x - radius*sin(running_theta), rect_end_y + radius*cos(running_theta)}; + running_theta += delta_theta; + } + offset += num_segments; + + locations[offset] = {rect_start_x, radius}; + offset++; + running_theta = 0; + for (unsigned idx=0; idx<=num_segments;idx++) + { + locations[offset + idx] = {rect_start_x - radius*cos(running_theta), radius *(1 - sin(running_theta))}; + running_theta += delta_theta; + } + + unsigned num_edges_per_fan = 2*num_segments + 1; + unsigned num_inner_rect_edges = 3*num_fans; + EdgeIds edge_ids(num_edges_per_fan*num_fans + num_inner_rect_edges + 1); + + // Fan edges + for (unsigned jdx=0; jdx< num_fans; jdx++) + { + unsigned node_offset = jdx*num_nodes_per_fan; + unsigned edge_offset = jdx*num_edges_per_fan; + + // Inner edges + for(unsigned idx=0; idx<=num_segments; idx++) + { + edge_ids[edge_offset + idx] = {node_offset, node_offset + idx + 1}; + } + + // Outer edges + for(unsigned idx=0; idx MeshPrimitives::buildRectangleAsLineMesh() { VecPoints locations = { diff --git a/src/mesh/MeshPrimitives.h b/src/mesh/MeshPrimitives.h index a151764..6c87c43 100644 --- a/src/mesh/MeshPrimitives.h +++ b/src/mesh/MeshPrimitives.h @@ -14,4 +14,6 @@ public: static std::unique_ptr buildExplodedGridAsTriMesh(unsigned numX, unsigned numY); static std::unique_ptr buildExplodedGridAsLineMesh(unsigned numX, unsigned numY); + + static std::unique_ptr buildRoundedRectangleAsTriMesh(double radius, double aspect_ratio = 1.0, unsigned num_segments = 4); }; diff --git a/src/mesh/Node.cpp b/src/mesh/Node.cpp index f35ba6f..fde89d5 100644 --- a/src/mesh/Node.cpp +++ b/src/mesh/Node.cpp @@ -42,4 +42,75 @@ std::vector Node::getVectorAttribute(const std::string& tag) const return {}; } +const Point& Node::getPoint() const +{ + return mPoint; +} + +void Node::scale(double x, double y) +{ + mPoint.scale(x, y); +} + +void Node::translate(double x, double y, double z) +{ + mPoint.translate(x, y, z); +} + +bool Node::isCoincident(Node* node) const +{ + return node->getPoint() == mPoint; +} + +void Node::setState(State state) +{ + mState = state; +} + +Node::State Node::getState() const +{ + return mState; +} + +void Node::clearConnectivity() +{ + mAssociatedEdgeIds.clear(); + mAssociatedFaceIds.clear(); +} + +unsigned Node::getNumConnectedEdges() const +{ + return mAssociatedEdgeIds.size(); +} + +unsigned Node::getNumConnectedFaces() const +{ + return mAssociatedFaceIds.size(); +} + +void Node::associateEdge(unsigned edgeId) +{ + mAssociatedEdgeIds.push_back(edgeId); +} + +void Node::associateFace(unsigned faceId) +{ + mAssociatedFaceIds.push_back(faceId); +} + +unsigned Node::getConnectedEdgeId(std::size_t idx) const +{ + return mAssociatedEdgeIds[idx]; +} + +unsigned Node::getConnectedFaceId(std::size_t idx) const +{ + return mAssociatedFaceIds[idx]; +} + +void Node::setIndex(std::size_t idx) +{ + mIndex = idx; +} + diff --git a/src/mesh/Node.h b/src/mesh/Node.h index 748dab1..d91e7e1 100644 --- a/src/mesh/Node.h +++ b/src/mesh/Node.h @@ -6,41 +6,65 @@ #include #include - class Node { public: + + enum class State + { + HEALTHY, + DIRTY + }; + Node(const Point& p, unsigned index = 0); static std::unique_ptr Create(const Point& p, unsigned index = 0); ~Node(); - unsigned getIndex() const; + void associateEdge(unsigned edgeId); - void updateIndex(unsigned index); - - const Point& getPoint() const - { - return mPoint; - } + void associateFace(unsigned faceId); void addVectorAttribute(const std::string& tag, const std::vector& values); + void clearConnectivity(); + + unsigned getIndex() const; + + unsigned getNumConnectedEdges() const; + + unsigned getNumConnectedFaces() const; + + unsigned getConnectedEdgeId(std::size_t idx) const; + + unsigned getConnectedFaceId(std::size_t idx) const; + + State getState() const; + + const Point& getPoint() const; + std::vector getVectorAttribute(const std::string& tag) const; - void scale(double x, double y) - { - mPoint.scale(x, y); - } + bool isCoincident(Node* node) const; - void translate(double x, double y, double z = 0.0) - { - mPoint.translate(x, y, z); - } + void setState(State state); + + void setIndex(std::size_t idx); + + void updateIndex(unsigned index); + + void scale(double x, double y); + + void translate(double x, double y, double z = 0.0); private: std::unordered_map > mVectorAttributes; unsigned mIndex{0}; Point mPoint; + + std::vector mAssociatedEdgeIds; + std::vector mAssociatedFaceIds; + + State mState{State::HEALTHY}; }; diff --git a/src/mesh/TriFace.cpp b/src/mesh/TriFace.cpp index 461a0fb..a61b3d2 100644 --- a/src/mesh/TriFace.cpp +++ b/src/mesh/TriFace.cpp @@ -25,3 +25,51 @@ std::vector TriFace::getNodeIds() const { return {mEdge0->getNode0Id(), mEdge0->getNode1Id(), mEdge1->getNode1Id()}; } + +unsigned TriFace::getNumNodes() const +{ + return 3; +} + +void TriFace::replaceEdge(Edge* original, Edge* replacement) +{ + if (original == mEdge0) + { + mEdge0 = replacement; + } + else if (original == mEdge1) + { + mEdge1 = replacement; + } + else if (original == mEdge2) + { + mEdge2 = replacement; + } +} + +unsigned TriFace::getEdge0Id () const +{ + return mEdge0->getId(); +} + +unsigned TriFace::getEdge1Id () const +{ + return mEdge1->getId(); +} + +unsigned TriFace::getEdge2Id () const +{ + return mEdge2->getId(); +} + +std::vector TriFace::getEdgeIds() const +{ + return {mEdge0->getId(), mEdge1->getId(), mEdge2->getId()}; +} + +void TriFace::associateWidthEdges() +{ + mEdge0->associateFace(mId); + mEdge1->associateFace(mId); + mEdge2->associateFace(mId); +} diff --git a/src/mesh/TriFace.h b/src/mesh/TriFace.h index 32d70e9..7878117 100644 --- a/src/mesh/TriFace.h +++ b/src/mesh/TriFace.h @@ -11,12 +11,22 @@ public: ~TriFace(); - std::vector getNodeIds() const override; - static std::unique_ptr Create(Edge* edge0, Edge* edge1, Edge* edge2, unsigned id=0); -private: + void associateWidthEdges() override; + std::vector getNodeIds() const override; + + unsigned getNumNodes() const override; + + void replaceEdge(Edge* original, Edge* replacement); + + unsigned getEdge0Id () const; + unsigned getEdge1Id () const; + unsigned getEdge2Id () const; + std::vector getEdgeIds() const override; + +private: Edge* mEdge0{nullptr}; Edge* mEdge1{nullptr}; Edge* mEdge2{nullptr}; diff --git a/src/mesh/TriMesh.cpp b/src/mesh/TriMesh.cpp index 5251982..18c29f4 100644 --- a/src/mesh/TriMesh.cpp +++ b/src/mesh/TriMesh.cpp @@ -1,6 +1,5 @@ #include "TriMesh.h" -#include "Node.h" #include "Edge.h" #include "TriFace.h" @@ -9,43 +8,39 @@ TriMesh::~TriMesh() } -void TriMesh::populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces) +AbstractMesh::MeshType TriMesh::getType() const { - mNodes = std::move(nodes); - mEdges = std::move(edges); - mFaces = std::move(faces); + return AbstractMesh::MeshType::TRI; } -std::vector TriMesh::getFaceNodeIds() const +std::unique_ptr TriMesh::copy() const { - unsigned nodes_per_face = 3; - std::vector ids(nodes_per_face*mFaces.size()); - - for(std::size_t idx=0; idxgetNodeIds(); - for(std::size_t jdx=0; jdx(node->getPoint()); + count++; } - return ids; -} -std::vector > TriMesh::getFaceVectorAttributes(const std::string& tag) -{ - std::vector > attribs(mFaces.size()); - for(std::size_t idx=0; idxgetVectorAttribute(tag)}; + edges[count] = std::make_unique(nodes[edge->getNode0Id()].get(), nodes[edge->getNode0Id()].get()); + count++; } - return attribs; -} -void TriMesh::addConstantFaceVectorAttribute(const std::string& tag, const std::vector& values) -{ - for (const auto& face : mFaces) + VecFaces faces(mFaces.size()); + count = 0; + for (auto& face : mFaces) { - face->addVectorAttribute(tag, values); + auto ids = face->getEdgeIds(); + faces[count] = std::make_unique(edges[ids[0]].get(), edges[ids[1]].get(), edges[ids[2]].get()); + count++; } + + auto mesh = std::make_unique(); + mesh->populate(nodes, edges, faces); + return mesh; } diff --git a/src/mesh/TriMesh.h b/src/mesh/TriMesh.h index 79ce181..63700ee 100644 --- a/src/mesh/TriMesh.h +++ b/src/mesh/TriMesh.h @@ -1,40 +1,15 @@ #pragma once -#include "AbstractMesh.h" +#include "FaceMesh.h" -#include -#include - -class Edge; -class TriFace; - -using EdgePtr = std::unique_ptr; -using TriFacePtr = std::unique_ptr; - -using VecEdges = std::vector; -using VecFaces = std::vector; - -class TriMesh : public AbstractMesh +class TriMesh : public FaceMesh { public: TriMesh() = default; ~TriMesh(); - void populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces); + std::unique_ptr copy() const override; - std::vector getFaceNodeIds() const; - - void addConstantFaceVectorAttribute(const std::string& tag, const std::vector& values); - - std::vector > getFaceVectorAttributes(const std::string& tag); - - MeshType getType() const - { - return MeshType::TRI; - } - -private: - VecEdges mEdges; - VecFaces mFaces; + MeshType getType() const; }; diff --git a/src/ui_elements/CMakeLists.txt b/src/ui_elements/CMakeLists.txt index 81e0308..c3a7619 100644 --- a/src/ui_elements/CMakeLists.txt +++ b/src/ui_elements/CMakeLists.txt @@ -16,6 +16,7 @@ list(APPEND ui_elements_LIB_INCLUDES ui_events/ResizeEvent.cpp widgets/Widget.cpp widgets/Button.cpp + widgets/ButtonGroup.cpp widgets/Label.cpp widgets/HorizontalSpacer.cpp widgets/VerticalSpacer.cpp diff --git a/src/ui_elements/widgets/ButtonGroup.cpp b/src/ui_elements/widgets/ButtonGroup.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/ui_elements/widgets/ButtonGroup.h b/src/ui_elements/widgets/ButtonGroup.h new file mode 100644 index 0000000..e866a5e --- /dev/null +++ b/src/ui_elements/widgets/ButtonGroup.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Widget.h" + +class ButtonGroup : public Widget +{ +public: + + enum class Orientation + { + HORIZONTAL, + VERTICAL + }; + + ButtonGroup(); + + ~ButtonGroup(); + + static std::unique_ptr Create(); + +private: + bool mExclusiveActivation{true}; +};