Start building mesh primitives.
This commit is contained in:
parent
a20c0183df
commit
fcd90b5db4
30 changed files with 856 additions and 97 deletions
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
6
apps/sample-gui/canvas/CanvasController.cpp
Normal file
6
apps/sample-gui/canvas/CanvasController.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "CanvasController.h"
|
||||
|
||||
std::unique_ptr<CanvasController> CanvasController::Create()
|
||||
{
|
||||
return std::make_unique<CanvasController>();
|
||||
}
|
9
apps/sample-gui/canvas/CanvasController.h
Normal file
9
apps/sample-gui/canvas/CanvasController.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
class CanvasController
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<CanvasController> Create();
|
||||
};
|
75
apps/sample-gui/canvas/CanvasView.cpp
Normal file
75
apps/sample-gui/canvas/CanvasView.cpp
Normal file
|
@ -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 <iostream>
|
||||
|
||||
CanvasView::CanvasView()
|
||||
: mController(CanvasController::Create())
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
CanvasView::~CanvasView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<CanvasView> CanvasView::Create()
|
||||
{
|
||||
return std::make_unique<CanvasView>();
|
||||
}
|
||||
|
||||
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<Widget> 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;
|
||||
}
|
||||
|
26
apps/sample-gui/canvas/CanvasView.h
Normal file
26
apps/sample-gui/canvas/CanvasView.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.h"
|
||||
|
||||
class CanvasController;
|
||||
|
||||
class CanvasView : public Widget
|
||||
{
|
||||
public:
|
||||
CanvasView();
|
||||
|
||||
~CanvasView();
|
||||
|
||||
static std::unique_ptr<CanvasView> Create();
|
||||
|
||||
private:
|
||||
|
||||
void initialize();
|
||||
|
||||
std::unique_ptr<Widget> initializeCacheButtons();
|
||||
|
||||
//std::unique_ptr<Widget> initializeCacheButtons();
|
||||
|
||||
std::unique_ptr<CanvasController> mController;
|
||||
};
|
||||
using CanvasViewPtr = std::unique_ptr<CanvasView>;
|
|
@ -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<TextEditorController>;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -5,21 +5,23 @@
|
|||
#include "Label.h"
|
||||
#include "TextBox.h"
|
||||
#include "TextEditorController.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class TextEditorView : public Widget
|
||||
{
|
||||
TextBox* mTextBox;
|
||||
TextEditorControllerUPtr mController;
|
||||
|
||||
public:
|
||||
|
||||
TextEditorView();
|
||||
|
||||
static std::unique_ptr<TextEditorView> Create();
|
||||
|
||||
TextEditorController* GetController();
|
||||
TextEditorController* getController();
|
||||
|
||||
void Initialize();
|
||||
void initialize();
|
||||
|
||||
private:
|
||||
TextBox* mTextBox;
|
||||
TextEditorControllerUPtr mController;
|
||||
};
|
||||
using TextEditorViewUPtr = std::unique_ptr<TextEditorView>;
|
||||
|
|
|
@ -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<double>& values)
|
||||
{
|
||||
mVectorAttributes[tag] = values;
|
||||
|
@ -20,3 +27,8 @@ std::vector<double> AbstractFace::getVectorAttribute(const std::string& tag) con
|
|||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void AbstractFace::setIndex(std::size_t idx)
|
||||
{
|
||||
mId = idx;
|
||||
}
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
class Edge;
|
||||
|
||||
class AbstractFace
|
||||
{
|
||||
public:
|
||||
AbstractFace(unsigned id=0);
|
||||
|
||||
virtual ~AbstractFace() = default;
|
||||
virtual ~AbstractFace();
|
||||
|
||||
virtual std::vector<unsigned> getNodeIds() const = 0;
|
||||
|
||||
|
@ -18,6 +20,16 @@ public:
|
|||
|
||||
std::vector<double> 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<unsigned> getEdgeIds() const = 0;
|
||||
|
||||
protected:
|
||||
unsigned mId{0};
|
||||
std::unordered_map<std::string, std::vector<double> > mVectorAttributes;
|
||||
|
|
|
@ -40,6 +40,8 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<AbstractMesh> copy() const = 0;
|
||||
|
||||
std::vector<std::vector<double> > getNodeVectorAttributes(const std::string& tag);
|
||||
|
||||
std::vector<double> getVectorAttribute(const std::string& tag) const;
|
||||
|
|
|
@ -7,6 +7,7 @@ list(APPEND mesh_LIB_INCLUDES
|
|||
Node.cpp
|
||||
QuadMesh.cpp
|
||||
TriMesh.cpp
|
||||
FaceMesh.cpp
|
||||
LineMesh.cpp
|
||||
MeshPrimitives.cpp
|
||||
MeshBuilder.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;
|
||||
}
|
||||
|
|
|
@ -1,23 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
class Node;
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class Node;
|
||||
|
||||
class Edge
|
||||
{
|
||||
public:
|
||||
enum class State
|
||||
{
|
||||
HEALTHY,
|
||||
DIRTY
|
||||
};
|
||||
|
||||
Edge(Node* node0, Node* node1, unsigned id = 0);
|
||||
|
||||
~Edge();
|
||||
|
||||
static std::unique_ptr<Edge> 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<unsigned> mAssociatedFaceIds;
|
||||
|
||||
State mState{State::HEALTHY};
|
||||
};
|
||||
|
|
182
src/mesh/FaceMesh.cpp
Normal file
182
src/mesh/FaceMesh.cpp
Normal file
|
@ -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<unsigned> FaceMesh::getFaceNodeIds() const
|
||||
{
|
||||
if (mFaces.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto nodes_per_face = mFaces[0]->getNumNodes();
|
||||
std::vector<unsigned> ids(nodes_per_face*mFaces.size());
|
||||
|
||||
for(std::size_t idx=0; idx<mFaces.size(); idx++)
|
||||
{
|
||||
const auto nodeIds = mFaces[idx]->getNodeIds();
|
||||
for(std::size_t jdx=0; jdx<nodes_per_face; jdx++)
|
||||
{
|
||||
ids[nodes_per_face*idx + jdx] = nodeIds[jdx];
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
std::vector<std::vector<double> > FaceMesh::getFaceVectorAttributes(const std::string& tag)
|
||||
{
|
||||
std::vector<std::vector<double> > attribs(mFaces.size());
|
||||
for(std::size_t idx=0; idx<mFaces.size(); idx++)
|
||||
{
|
||||
attribs[idx] = {mFaces[idx]->getVectorAttribute(tag)};
|
||||
}
|
||||
return attribs;
|
||||
}
|
||||
|
||||
void FaceMesh::addConstantFaceVectorAttribute(const std::string& tag, const std::vector<double>& 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<FaceMesh> 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();
|
||||
}
|
47
src/mesh/FaceMesh.h
Normal file
47
src/mesh/FaceMesh.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractMesh.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class Edge;
|
||||
class AbstractFace;
|
||||
|
||||
using EdgePtr = std::unique_ptr<Edge>;
|
||||
using AbstractFacePtr = std::unique_ptr<AbstractFace>;
|
||||
|
||||
using VecEdges = std::vector<EdgePtr>;
|
||||
using VecFaces = std::vector<AbstractFacePtr>;
|
||||
|
||||
class FaceMesh : public AbstractMesh
|
||||
{
|
||||
public:
|
||||
FaceMesh() = default;
|
||||
|
||||
~FaceMesh();
|
||||
|
||||
virtual std::unique_ptr<AbstractMesh> copy() const = 0;
|
||||
|
||||
void populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces);
|
||||
|
||||
std::vector<unsigned> getFaceNodeIds() const;
|
||||
|
||||
void addConstantFaceVectorAttribute(const std::string& tag, const std::vector<double>& values);
|
||||
|
||||
std::vector<std::vector<double> > getFaceVectorAttributes(const std::string& tag);
|
||||
|
||||
void merge(std::unique_ptr<FaceMesh> mesh);
|
||||
|
||||
protected:
|
||||
void resetConnectivity();
|
||||
|
||||
void resetIds();
|
||||
|
||||
void replaceIfOverlapping(FaceMesh* mesh, Node* node) const;
|
||||
|
||||
void replaceIfOverlapping(FaceMesh* mesh, Edge* edge) const;
|
||||
|
||||
VecEdges mEdges;
|
||||
VecFaces mFaces;
|
||||
};
|
|
@ -13,6 +13,16 @@ void LineMesh::populate(VecNodes& nodes, VecEdges& edges)
|
|||
mEdges = std::move(edges);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractMesh> LineMesh::copy() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LineMesh::MeshType LineMesh::getType() const
|
||||
{
|
||||
return LineMesh::MeshType::LINE;
|
||||
}
|
||||
|
||||
std::vector<unsigned> LineMesh::getEdgeNodeIds() const
|
||||
{
|
||||
std::vector<unsigned> ids(2*mEdges.size());
|
||||
|
|
|
@ -20,10 +20,9 @@ public:
|
|||
|
||||
std::vector<unsigned> getEdgeNodeIds() const;
|
||||
|
||||
MeshType getType() const override
|
||||
{
|
||||
return MeshType::LINE;
|
||||
}
|
||||
MeshType getType() const override;
|
||||
|
||||
std::unique_ptr<AbstractMesh> copy() const override;
|
||||
|
||||
private:
|
||||
VecEdges mEdges;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "MeshBuilder.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
std::unique_ptr<TriMesh> MeshPrimitives::buildRectangleAsTriMesh()
|
||||
|
@ -29,6 +31,85 @@ std::unique_ptr<TriMesh> MeshPrimitives::buildRectangleAsTriMesh()
|
|||
return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids);
|
||||
}
|
||||
|
||||
std::unique_ptr<TriMesh> 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<num_segments; idx++)
|
||||
{
|
||||
edge_ids[edge_offset + num_segments + 1 + idx] = {node_offset + idx + 1, node_offset + idx + 2};
|
||||
}
|
||||
}
|
||||
|
||||
// Inner rect edges
|
||||
unsigned edge_offset = num_edges_per_fan*num_fans;
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<LineMesh> MeshPrimitives::buildRectangleAsLineMesh()
|
||||
{
|
||||
VecPoints locations = {
|
||||
|
|
|
@ -14,4 +14,6 @@ public:
|
|||
static std::unique_ptr<TriMesh> buildExplodedGridAsTriMesh(unsigned numX, unsigned numY);
|
||||
|
||||
static std::unique_ptr<LineMesh> buildExplodedGridAsLineMesh(unsigned numX, unsigned numY);
|
||||
|
||||
static std::unique_ptr<TriMesh> buildRoundedRectangleAsTriMesh(double radius, double aspect_ratio = 1.0, unsigned num_segments = 4);
|
||||
};
|
||||
|
|
|
@ -42,4 +42,75 @@ std::vector<double> 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,41 +6,65 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
|
||||
enum class State
|
||||
{
|
||||
HEALTHY,
|
||||
DIRTY
|
||||
};
|
||||
|
||||
Node(const Point& p, unsigned index = 0);
|
||||
|
||||
static std::unique_ptr<Node> 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<double>& 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<double> 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<std::string, std::vector<double> > mVectorAttributes;
|
||||
unsigned mIndex{0};
|
||||
Point mPoint;
|
||||
|
||||
std::vector<unsigned> mAssociatedEdgeIds;
|
||||
std::vector<unsigned> mAssociatedFaceIds;
|
||||
|
||||
State mState{State::HEALTHY};
|
||||
};
|
||||
|
|
|
@ -25,3 +25,51 @@ std::vector<unsigned> 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<unsigned> TriFace::getEdgeIds() const
|
||||
{
|
||||
return {mEdge0->getId(), mEdge1->getId(), mEdge2->getId()};
|
||||
}
|
||||
|
||||
void TriFace::associateWidthEdges()
|
||||
{
|
||||
mEdge0->associateFace(mId);
|
||||
mEdge1->associateFace(mId);
|
||||
mEdge2->associateFace(mId);
|
||||
}
|
||||
|
|
|
@ -11,12 +11,22 @@ public:
|
|||
|
||||
~TriFace();
|
||||
|
||||
std::vector<unsigned> getNodeIds() const override;
|
||||
|
||||
static std::unique_ptr<TriFace> Create(Edge* edge0, Edge* edge1, Edge* edge2, unsigned id=0);
|
||||
|
||||
private:
|
||||
void associateWidthEdges() override;
|
||||
|
||||
std::vector<unsigned> getNodeIds() const override;
|
||||
|
||||
unsigned getNumNodes() const override;
|
||||
|
||||
void replaceEdge(Edge* original, Edge* replacement);
|
||||
|
||||
unsigned getEdge0Id () const;
|
||||
unsigned getEdge1Id () const;
|
||||
unsigned getEdge2Id () const;
|
||||
std::vector<unsigned> getEdgeIds() const override;
|
||||
|
||||
private:
|
||||
Edge* mEdge0{nullptr};
|
||||
Edge* mEdge1{nullptr};
|
||||
Edge* mEdge2{nullptr};
|
||||
|
|
|
@ -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<unsigned> TriMesh::getFaceNodeIds() const
|
||||
std::unique_ptr<AbstractMesh> TriMesh::copy() const
|
||||
{
|
||||
unsigned nodes_per_face = 3;
|
||||
std::vector<unsigned> ids(nodes_per_face*mFaces.size());
|
||||
VecNodes nodes(mNodes.size());
|
||||
unsigned count = 0;
|
||||
for (auto& node : mNodes)
|
||||
{
|
||||
nodes[count] = std::make_unique<Node>(node->getPoint());
|
||||
count++;
|
||||
}
|
||||
|
||||
for(std::size_t idx=0; idx<mFaces.size(); idx++)
|
||||
VecEdges edges(mEdges.size());
|
||||
count = 0;
|
||||
for (auto& edge : mEdges)
|
||||
{
|
||||
const auto nodeIds = mFaces[idx]->getNodeIds();
|
||||
for(std::size_t jdx=0; jdx<nodes_per_face; jdx++)
|
||||
{
|
||||
ids[nodes_per_face*idx + jdx] = nodeIds[jdx];
|
||||
edges[count] = std::make_unique<Edge>(nodes[edge->getNode0Id()].get(), nodes[edge->getNode0Id()].get());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
std::vector<std::vector<double> > TriMesh::getFaceVectorAttributes(const std::string& tag)
|
||||
{
|
||||
std::vector<std::vector<double> > attribs(mFaces.size());
|
||||
for(std::size_t idx=0; idx<mFaces.size(); idx++)
|
||||
VecFaces faces(mFaces.size());
|
||||
count = 0;
|
||||
for (auto& face : mFaces)
|
||||
{
|
||||
attribs[idx] = {mFaces[idx]->getVectorAttribute(tag)};
|
||||
auto ids = face->getEdgeIds();
|
||||
faces[count] = std::make_unique<TriFace>(edges[ids[0]].get(), edges[ids[1]].get(), edges[ids[2]].get());
|
||||
count++;
|
||||
}
|
||||
return attribs;
|
||||
}
|
||||
|
||||
void TriMesh::addConstantFaceVectorAttribute(const std::string& tag, const std::vector<double>& values)
|
||||
{
|
||||
for (const auto& face : mFaces)
|
||||
{
|
||||
face->addVectorAttribute(tag, values);
|
||||
}
|
||||
auto mesh = std::make_unique<TriMesh>();
|
||||
mesh->populate(nodes, edges, faces);
|
||||
return mesh;
|
||||
}
|
||||
|
|
|
@ -1,40 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractMesh.h"
|
||||
#include "FaceMesh.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class Edge;
|
||||
class TriFace;
|
||||
|
||||
using EdgePtr = std::unique_ptr<Edge>;
|
||||
using TriFacePtr = std::unique_ptr<TriFace>;
|
||||
|
||||
using VecEdges = std::vector<EdgePtr>;
|
||||
using VecFaces = std::vector<TriFacePtr>;
|
||||
|
||||
class TriMesh : public AbstractMesh
|
||||
class TriMesh : public FaceMesh
|
||||
{
|
||||
public:
|
||||
TriMesh() = default;
|
||||
|
||||
~TriMesh();
|
||||
|
||||
void populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces);
|
||||
std::unique_ptr<AbstractMesh> copy() const override;
|
||||
|
||||
std::vector<unsigned> getFaceNodeIds() const;
|
||||
|
||||
void addConstantFaceVectorAttribute(const std::string& tag, const std::vector<double>& values);
|
||||
|
||||
std::vector<std::vector<double> > getFaceVectorAttributes(const std::string& tag);
|
||||
|
||||
MeshType getType() const
|
||||
{
|
||||
return MeshType::TRI;
|
||||
}
|
||||
|
||||
private:
|
||||
VecEdges mEdges;
|
||||
VecFaces mFaces;
|
||||
MeshType getType() const;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
0
src/ui_elements/widgets/ButtonGroup.cpp
Normal file
0
src/ui_elements/widgets/ButtonGroup.cpp
Normal file
23
src/ui_elements/widgets/ButtonGroup.h
Normal file
23
src/ui_elements/widgets/ButtonGroup.h
Normal file
|
@ -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<ButtonGroup> Create();
|
||||
|
||||
private:
|
||||
bool mExclusiveActivation{true};
|
||||
};
|
Loading…
Reference in a new issue