Clean project structure.

This commit is contained in:
jmsgrogan 2023-01-17 10:13:25 +00:00
parent 78a4fa99ff
commit 947bf937fd
496 changed files with 206 additions and 137 deletions

View file

@ -0,0 +1,34 @@
#include "AbstractFace.h"
#include "Edge.h"
AbstractFace::AbstractFace(std::size_t id)
: mId(id)
{
}
AbstractFace::~AbstractFace()
{
}
void AbstractFace::addVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
mVectorAttributes[tag] = values;
}
std::vector<double> AbstractFace::getVectorAttribute(const std::string& tag) const
{
auto iter = mVectorAttributes.find(tag);
if (iter != mVectorAttributes.end())
{
return iter->second;
}
return {};
}
void AbstractFace::setIndex(std::size_t idx)
{
mId = idx;
}

View file

@ -0,0 +1,47 @@
#pragma once
#include "Point.h"
#include <memory>
#include <vector>
#include <unordered_map>
#include <string>
class Edge;
class AbstractFace
{
public:
enum class Orientation
{
CW,
CCW
};
AbstractFace(std::size_t id=0);
virtual ~AbstractFace();
virtual std::vector<std::size_t> getNodeIds() const = 0;
void addVectorAttribute(const std::string& tag, const std::vector<double>& values);
std::vector<double> getVectorAttribute(const std::string& tag) const;
virtual std::size_t getNumNodes() const = 0;
virtual void associateWidthEdges() = 0;
virtual std::vector<std::size_t> getEdgeIds() const = 0;
virtual std::vector<Point> getNodeLocations(Orientation orientation = Orientation::CCW) const = 0;
virtual void replaceEdge(Edge* original, Edge* replacement) = 0;
void setIndex(std::size_t idx);
protected:
std::size_t mId{0};
std::unordered_map<std::string, std::vector<double> > mVectorAttributes;
};

View file

@ -0,0 +1,75 @@
#include "AbstractMesh.h"
void AbstractMesh::addConstantNodeVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
}
std::vector<std::vector<double> > AbstractMesh::getNodeVectorAttributes(const std::string& tag)
{
std::vector<std::vector<double> > attribs(mNodes.size());
return attribs;
}
void AbstractMesh::addVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
mVectorAttributes[tag] = values;
}
bool AbstractMesh::hasVectorAttribute(const std::string& tag) const
{
return mVectorAttributes.find(tag) != mVectorAttributes.end();
}
unsigned AbstractMesh::getNumNodes() const
{
return unsigned(mNodes.size());
}
const VecNodes& AbstractMesh::getNodes() const
{
return mNodes;
}
std::vector<double> AbstractMesh::getVectorAttribute(const std::string& tag) const
{
auto iter = mVectorAttributes.find(tag);
if (iter != mVectorAttributes.end())
{
return iter->second;
}
return {};
}
void AbstractMesh::scale(double scaleX, double scaleY)
{
Transform transform({ 0.0, 0.0 }, scaleX, scaleY);
for (auto& node : mNodes)
{
node->apply(transform);
}
}
void AbstractMesh::transform(const Transform& transform)
{
for (auto& node : mNodes)
{
node->apply(transform);
}
}
void AbstractMesh::translate(double offsetX, double offsetY, double offsetZ)
{
const Point loc {-offsetX, -offsetY, -offsetZ};
Transform transform(loc);
for (auto& node : mNodes)
{
node->apply(transform);
}
}
void AbstractMesh::translate(const Point& offset)
{
translate(offset.getX(), offset.getY(), offset.getZ());
}

View file

@ -0,0 +1,68 @@
#pragma once
#include "Point.h"
#include "Node.h"
#include "Transform.h"
#include <memory>
#include <vector>
using NodePtr = std::unique_ptr<Node>;
using VecNodes = std::vector<NodePtr>;
class AbstractMesh
{
public:
enum class MeshType
{
LINE,
TRI,
QUAD
};
virtual ~AbstractMesh() = default;
void addVectorAttribute(const std::string& tag, const std::vector<double>& values);
void addConstantNodeVectorAttribute(const std::string& tag, const std::vector<double>& values);
template<typename T>
std::vector<T> getVerticesFlat(T scaleX = 1.0, T scaleY = 1.0, T scaleZ = 1.0) const
{
std::vector<T> ret(3*mNodes.size());
for(std::size_t idx = 0; idx<mNodes.size(); idx++)
{
auto node = mNodes[idx].get();
ret[3*idx] = node->getPoint().getX()/scaleX;
ret[3*idx + 1] = node->getPoint().getY()/scaleY;
ret[3*idx + 2] = node->getPoint().getZ()/scaleZ;
}
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;
virtual MeshType getType() const = 0;
bool hasVectorAttribute(const std::string& tag) const;
void scale(double scaleX, double scaleY);
void translate(const Point& offset);
void translate(double offsetX, double offsetY, double offsetZ = 0.0);
void transform(const Transform& transform);
unsigned getNumNodes() const;
const VecNodes& getNodes() const;
protected:
std::unordered_map<std::string, std::vector<double> > mVectorAttributes;
VecNodes mNodes;
};

View file

@ -0,0 +1,38 @@
list(APPEND mesh_LIB_INCLUDES
AbstractMesh.cpp
AbstractMesh.h
Edge.cpp
Edge.h
AbstractFace.cpp
AbstractFace.h
QuadFace.cpp
QuadFace.h
TriFace.cpp
TriFace.h
Node.cpp
Node.h
QuadMesh.cpp
QuadMesh.h
TriMesh.cpp
TriMesh.h
FaceMesh.cpp
FaceMesh.h
LineMesh.cpp
LineMesh.h
MeshPrimitives.cpp
MeshPrimitives.h
MeshBuilder.cpp
MeshBuilder.h
MeshObjWriter.h
MeshObjWriter.cpp
)
# add the library
add_library(mesh SHARED ${mesh_LIB_INCLUDES})
target_link_libraries(mesh core geometry)
target_include_directories(mesh PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/")
set_target_properties( mesh PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
set_property(TARGET mesh PROPERTY FOLDER src/rendering)

122
src/rendering/mesh/Edge.cpp Normal file
View file

@ -0,0 +1,122 @@
#include "Edge.h"
#include "Node.h"
Edge::Edge(Node* node0, Node* node1, std::size_t id)
: mNode0(node0),
mNode1(node1),
mId(id)
{
}
std::unique_ptr<Edge> Edge::Create(Node* node0, Node* node1, std::size_t id)
{
return std::make_unique<Edge>(node0, node1, id);
}
Edge::~Edge()
{
}
std::size_t Edge::getNode0Id() const
{
return mNode0->getIndex();
}
std::size_t 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;
}
}
std::size_t Edge::getId() const
{
return mId;
}
void Edge::setState(State state)
{
mState = state;
}
Edge::State Edge::getState() const
{
return mState;
}
void Edge::clearConnectivity()
{
mAssociatedFaceIds.clear();
}
std::size_t Edge::getNumConnectedFaces() const
{
return mAssociatedFaceIds.size();
}
void Edge::associateFace(std::size_t faceId)
{
mAssociatedFaceIds.push_back(faceId);
}
std::size_t Edge::getConnectedFaceId(std::size_t idx) const
{
return mAssociatedFaceIds[idx];
}
void Edge::associateWithNodes()
{
mNode0->associateEdge(mId);
mNode1->associateEdge(mId);
}
void Edge::setIndex(std::size_t idx)
{
mId = idx;
}

65
src/rendering/mesh/Edge.h Normal file
View file

@ -0,0 +1,65 @@
#pragma once
#include <memory>
#include <vector>
class Node;
class Edge
{
public:
enum class State
{
HEALTHY,
DIRTY
};
Edge(Node* node0, Node* node1, std::size_t id = 0);
~Edge();
static std::unique_ptr<Edge> Create(Node* node0, Node* node1, std::size_t id = 0);
void associateFace(std::size_t faceId);
void associateWithNodes();
void clearConnectivity();
std::size_t getConnectedFaceId(std::size_t idx) const;
State getState() const;
std::size_t getId() const;
std::size_t getNode0Id() const;
std::size_t getNode1Id() const;
Node* getNode0() const;
Node* getNode1() const;
std::size_t 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(std::size_t idx);
private:
std::size_t mId{0};
Node* mNode0{nullptr};
Node* mNode1{nullptr};
std::vector<std::size_t> mAssociatedFaceIds;
State mState{State::HEALTHY};
};

View file

@ -0,0 +1,201 @@
#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();
}
const VecFaces& FaceMesh::getFaces() const
{
return mFaces;
}
std::vector<std::size_t> FaceMesh::getFaceNodeIds() const
{
if (mFaces.empty())
{
return {};
}
auto nodes_per_face = mFaces[0]->getNumNodes();
std::vector<std::size_t> 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()
{
std::size_t count = 0;
for (auto& node : mNodes)
{
node->setIndex(count);
count++;
}
count = 0;
for (auto& edge : mEdges)
{
edge->setIndex(count);
count++;
}
count = 0;
for (auto& face : mFaces)
{
face->setIndex(count);
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 (std::size_t 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 (std::size_t idx=0; idx<2; idx++)
{
auto face = mesh->mFaces[idx].get();
face->replaceEdge(target_edge, edge.get());
}
break;
}
}
}
std::vector<std::size_t> FaceMesh::getEdgeNodeIds() const
{
std::vector<std::size_t> ids(2*mEdges.size());
for(std::size_t idx=0; idx<mEdges.size(); idx++)
{
ids[2*idx] = mEdges[idx]->getNode0Id();
ids[2*idx + 1] = mEdges[idx]->getNode1Id();
}
return ids;
}
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();
}

View file

@ -0,0 +1,51 @@
#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<std::size_t> getFaceNodeIds() const;
std::vector<std::size_t> getEdgeNodeIds() 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);
const VecFaces& getFaces() const;
protected:
void resetConnectivity();
void resetIds();
void replaceIfOverlapping(FaceMesh* mesh, Node* node) const;
void replaceIfOverlapping(FaceMesh* mesh, Edge* edge) const;
VecEdges mEdges;
VecFaces mFaces;
};

View file

@ -0,0 +1,35 @@
#include "LineMesh.h"
#include "Edge.h"
LineMesh::~LineMesh()
{
}
void LineMesh::populate(VecNodes& nodes, VecEdges& edges)
{
mNodes = std::move(nodes);
mEdges = std::move(edges);
}
std::unique_ptr<AbstractMesh> LineMesh::copy() const
{
return nullptr;
}
LineMesh::MeshType LineMesh::getType() const
{
return LineMesh::MeshType::LINE;
}
std::vector<std::size_t> LineMesh::getEdgeNodeIds() const
{
std::vector<std::size_t> ids(2*mEdges.size());
for(std::size_t idx=0; idx<mEdges.size(); idx++)
{
ids[2*idx] = mEdges[idx]->getNode0Id();
ids[2*idx + 1] = mEdges[idx]->getNode1Id();
}
return ids;
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "AbstractMesh.h"
#include <string>
#include <unordered_map>
class Edge;
using EdgePtr = std::unique_ptr<Edge>;
using VecEdges = std::vector<EdgePtr>;
class LineMesh : public AbstractMesh
{
public:
LineMesh() = default;
~LineMesh();
void populate(VecNodes& nodes, VecEdges& edges);
std::vector<std::size_t> getEdgeNodeIds() const;
MeshType getType() const override;
std::unique_ptr<AbstractMesh> copy() const override;
private:
VecEdges mEdges;
};

View file

@ -0,0 +1,55 @@
#include "MeshBuilder.h"
#include "Node.h"
#include "Edge.h"
#include "TriFace.h"
std::unique_ptr<TriMesh> MeshBuilder::buildTriMesh(const VecPoints& locations, const EdgeIds& edgeIds, const FaceIds& faceIds)
{
auto mesh = std::make_unique<TriMesh>();
VecNodes nodes(locations.size());
for (std::size_t idx=0; idx<locations.size(); idx++)
{
nodes[idx] = Node::Create(locations[idx], idx);
}
VecEdges edges(edgeIds.size());
for (std::size_t idx=0; idx<edgeIds.size(); idx++)
{
edges[idx] = Edge::Create(nodes[edgeIds[idx].first].get(), nodes[edgeIds[idx].second].get(), idx);
}
VecFaces faces(faceIds.size());
for (std::size_t idx=0; idx<faceIds.size(); idx++)
{
faces[idx] = TriFace::Create(
edges[faceIds[idx][0]].get(),
edges[faceIds[idx][1]].get(),
edges[faceIds[idx][2]].get(),
idx);
}
mesh->populate(nodes, edges, faces);
return mesh;
}
std::unique_ptr<LineMesh> MeshBuilder::buildLineMesh(const VecPoints& locations, const EdgeIds& edgeIds)
{
auto mesh = std::make_unique<LineMesh>();
VecNodes nodes(locations.size());
for (std::size_t idx=0; idx<locations.size(); idx++)
{
nodes[idx] = Node::Create(locations[idx], idx);
}
VecEdges edges(edgeIds.size());
for (std::size_t idx=0; idx<edgeIds.size(); idx++)
{
edges[idx] = Edge::Create(nodes[edgeIds[idx].first].get(), nodes[edgeIds[idx].second].get(), idx);
}
mesh->populate(nodes, edges);
return mesh;
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "TriMesh.h"
#include "LineMesh.h"
#include "Point.h"
#include <memory>
using EdgeIds = std::vector<std::pair<std::size_t, std::size_t> >;
using FaceIds = std::vector<std::vector<std::size_t> >;
using VecPoints = std::vector<Point>;
class MeshBuilder
{
public:
static std::unique_ptr<TriMesh> buildTriMesh(const VecPoints& locations, const EdgeIds& edgeIds, const FaceIds& faceIds);
static std::unique_ptr<LineMesh> buildLineMesh(const VecPoints& locations, const EdgeIds& edgeIds);
};

View file

@ -0,0 +1,32 @@
#include "MeshObjWriter.h"
#include "File.h"
#include "TriMesh.h"
#include "AbstractFace.h"
#include <sstream>
std::string MeshObjWriter::serialize(TriMesh* mesh)
{
std::stringstream output;
for (const auto& node : mesh->getNodes())
{
const auto x = node->getPoint().getX();
const auto y = node->getPoint().getY();
const auto z = node->getPoint().getZ();
output << "v "<< x << " " << y << " " << z << "\n";
}
for (const auto& face : mesh->getFaces())
{
auto ids = face->getNodeIds();
output << "f " << 1 + ids[0] << " " << 1 + ids[1] << " " << 1 + ids[2] << "\n";
}
return output.str();
}
void MeshObjWriter::write(const Path& path, TriMesh* mesh)
{
File file(path);
file.writeText(serialize(mesh));
}

View file

@ -0,0 +1,14 @@
#pragma once
#include <string>
#include <filesystem>
using Path = std::filesystem::path;
class TriMesh;
class MeshObjWriter
{
public:
static std::string serialize(TriMesh* mesh);
static void write(const Path& path, TriMesh* mesh);
};

View file

@ -0,0 +1,305 @@
#include "MeshPrimitives.h"
#include "MeshBuilder.h"
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
std::unique_ptr<TriMesh> MeshPrimitives::buildRectangleAsTriMesh()
{
VecPoints locations = {
{0, 0},
{1, 0},
{1, 1},
{0, 1}
};
EdgeIds edge_ids = {
{0, 1},
{1, 2},
{2, 0},
{2, 3},
{3, 0}
};
FaceIds face_ids = {
{0, 1, 2},
{2, 3, 4}
};
return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids);
}
std::unique_ptr<TriMesh> MeshPrimitives::buildCircleAsTriMesh(std::size_t numSegments)
{
VecPoints locations(numSegments + 1);
locations[0] = {0, 0};
const double delta_theta = (2.0*M_PI)/double(numSegments);
double theta = 0.0;
for(std::size_t idx=1; idx<=numSegments; idx++)
{
const double x = sin(theta);
const double y = cos(theta);
locations[idx] = {x, y};
theta += delta_theta;
std::cout << "Adding node at: " << x << " | " << y << std::endl;
}
EdgeIds edge_ids(2*numSegments);
for(std::size_t idx=0; idx<numSegments; idx++)
{
edge_ids[idx] = {0, idx+1};
auto wrap_node = idx + 2;
if (wrap_node > numSegments)
{
wrap_node = 1;
}
edge_ids[idx + numSegments] = {idx + 1, wrap_node};
}
FaceIds face_ids(numSegments);
for(std::size_t idx=0; idx<numSegments; idx++)
{
auto top_edge_inner = idx + 1;
if (top_edge_inner == numSegments)
{
top_edge_inner = 0;
}
const auto outer_edge = idx + numSegments;
face_ids[idx] = {idx, outer_edge, top_edge_inner};
}
return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids);
}
std::unique_ptr<LineMesh> MeshPrimitives::buildCircleAsLineMesh(std::size_t numSegments)
{
VecPoints locations(numSegments);
const double delta_theta = (2.0*M_PI)/double(numSegments);
double theta = 0.0;
for(unsigned idx=0; idx<numSegments; idx++)
{
const double x = sin(theta);
const double y = cos(theta);
locations[idx] = {x, y};
}
EdgeIds edge_ids(2*numSegments);
for(unsigned idx=0; idx<numSegments; idx++)
{
auto top_node = idx + 1;
if (top_node == numSegments)
{
top_node = 0;
}
edge_ids[idx] = {idx, top_node};
}
return MeshBuilder::buildLineMesh(locations, edge_ids);
}
std::unique_ptr<TriMesh> MeshPrimitives::buildRoundedRectangleAsTriMesh(double radius, double aspect_ratio, std::size_t num_segments)
{
std::size_t num_fans = 4;
std::size_t 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};
std::size_t offset = 1;
for (std::size_t 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 (std::size_t 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 (std::size_t 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 (std::size_t 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;
}
std::size_t num_edges_per_fan = 2*num_segments + 1;
std::size_t num_inner_rect_edges = 3*num_fans;
EdgeIds edge_ids(num_edges_per_fan*num_fans + num_inner_rect_edges + 1);
// Fan edges
for (std::size_t jdx=0; jdx< num_fans; jdx++)
{
std::size_t node_offset = jdx*num_nodes_per_fan;
std::size_t edge_offset = jdx*num_edges_per_fan;
// Inner edges
for(std::size_t idx=0; idx<=num_segments; idx++)
{
edge_ids[edge_offset + idx] = {node_offset, node_offset + idx + 1};
}
// Outer edges
for(std::size_t 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
std::size_t edge_offset = num_edges_per_fan*num_fans;
return nullptr;
}
std::unique_ptr<LineMesh> MeshPrimitives::buildRectangleAsLineMesh()
{
VecPoints locations = {
{0, 0},
{1, 0},
{1, 1},
{0, 1}
};
EdgeIds edge_ids = {
{0, 1},
{1, 2},
{2, 3},
{3, 0}
};
return MeshBuilder::buildLineMesh(locations, edge_ids);
}
std::unique_ptr<TriMesh> MeshPrimitives::buildExplodedGridAsTriMesh(std::size_t numX, std::size_t numY)
{
double delta_x = 1.0/double(numX);
double delta_y = 1.0/double(numY);
VecPoints locations (4 * numX * numY);
double offset_x = delta_x/2.0;
double offset_y = delta_y/2.0;
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
auto locX0 = offset_x - delta_x/2.0;
auto locX1 = offset_x + delta_x/2.0;
auto locY0 = offset_y - delta_y/2.0;
auto locY1 = offset_y + delta_y/2.0;
auto id_offset = 4* (jdx + numX*idx);
locations[id_offset] = Point(locX0, locY0);
locations[id_offset + 1] = Point(locX1, locY0);
locations[id_offset + 2] = Point(locX1, locY1);
locations[id_offset + 3] = Point(locX0, locY1);
offset_x += delta_x;
}
offset_x = delta_x/2.0;
offset_y += delta_y;
}
EdgeIds edge_ids(5 * numX * numY);
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
std::size_t node_offset = 4 * (jdx + numX * idx);
auto id_offset = 5* (jdx + numX*idx);
edge_ids[id_offset] = {node_offset, node_offset + 1};
edge_ids[id_offset + 1] = {node_offset + 1, node_offset + 2};
edge_ids[id_offset + 2] = {node_offset + 2, node_offset + 3};
edge_ids[id_offset + 3] = {node_offset + 3, node_offset};
edge_ids[id_offset + 4] = {node_offset + 2, node_offset};
}
}
FaceIds face_ids(2 *numX * numY);
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
std::size_t edge_offset = 5 * (jdx + numX * idx);
std::size_t face_offset = 2 * (jdx + numX * idx);
face_ids[face_offset] = {edge_offset, edge_offset + 1, edge_offset + 4};
face_ids[face_offset + 1] = {edge_offset + 4, edge_offset + 2, edge_offset + 3};
}
}
return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids);
}
std::unique_ptr<LineMesh> MeshPrimitives::buildExplodedGridAsLineMesh(std::size_t numX, std::size_t numY)
{
double delta_x = 1.0/double(numX);
double delta_y = 1.0/double(numY);
VecPoints locations (4 * numX * numY);
double offset_x = delta_x/2.0;
double offset_y = delta_y/2.0;
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
auto locX0 = offset_x - delta_x/2.0;
auto locX1 = offset_x + delta_x/2.0;
auto locY0 = offset_y - delta_y/2.0;
auto locY1 = offset_y + delta_y/2.0;
auto id_offset = 4* (jdx + numX*idx);
locations[id_offset] = Point(locX0, locY0);
locations[id_offset + 1] = Point(locX1, locY0);
locations[id_offset + 2] = Point(locX1, locY1);
locations[id_offset + 3] = Point(locX0, locY1);
offset_x += delta_x;
}
offset_x = delta_x/2.0;
offset_y += delta_y;
}
EdgeIds edge_ids(4 * numX * numY);
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
std::size_t node_offset = 4 * (jdx + numX * idx);
auto id_offset = 4* (jdx + numX*idx);
edge_ids[id_offset] = {node_offset, node_offset + 1};
edge_ids[id_offset + 1] = {node_offset + 1, node_offset + 2};
edge_ids[id_offset + 2] = {node_offset + 2, node_offset + 3};
edge_ids[id_offset + 3] = {node_offset + 3, node_offset};
}
}
return MeshBuilder::buildLineMesh(locations, edge_ids);
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "TriMesh.h"
#include "LineMesh.h"
class MeshPrimitives
{
public:
static std::unique_ptr<TriMesh> buildCircleAsTriMesh(std::size_t numSegments = 24);
static std::unique_ptr<LineMesh> buildCircleAsLineMesh(std::size_t numSegments = 24);
static std::unique_ptr<TriMesh> buildRectangleAsTriMesh();
static std::unique_ptr<LineMesh> buildRectangleAsLineMesh();
static std::unique_ptr<TriMesh> buildExplodedGridAsTriMesh(std::size_t numX, std::size_t numY);
static std::unique_ptr<LineMesh> buildExplodedGridAsLineMesh(std::size_t numX, std::size_t numY);
static std::unique_ptr<TriMesh> buildRoundedRectangleAsTriMesh(double radius, double aspect_ratio = 1.0, std::size_t num_segments = 4);
};

111
src/rendering/mesh/Node.cpp Normal file
View file

@ -0,0 +1,111 @@
#include "Node.h"
std::unique_ptr<Node> Node::Create(const Point& p, std::size_t index)
{
return std::make_unique<Node>(p, index);
}
Node::~Node()
{
}
Node::Node(const Point& p, std::size_t index)
: mPoint(p),
mIndex(index)
{
}
std::size_t Node::getIndex() const
{
return mIndex;
}
void Node::updateIndex(std::size_t index)
{
mIndex = index;
}
void Node::addVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
mVectorAttributes[tag] = values;
}
std::vector<double> Node::getVectorAttribute(const std::string& tag) const
{
auto iter = mVectorAttributes.find(tag);
if (iter != mVectorAttributes.end())
{
return iter->second;
}
return {};
}
const Point& Node::getPoint() const
{
return mPoint;
}
void Node::apply(const Transform& transform)
{
mPoint.apply(transform);
}
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();
}
std::size_t Node::getNumConnectedEdges() const
{
return mAssociatedEdgeIds.size();
}
std::size_t Node::getNumConnectedFaces() const
{
return mAssociatedFaceIds.size();
}
void Node::associateEdge(std::size_t edgeId)
{
mAssociatedEdgeIds.push_back(edgeId);
}
void Node::associateFace(std::size_t faceId)
{
mAssociatedFaceIds.push_back(faceId);
}
std::size_t Node::getConnectedEdgeId(std::size_t idx) const
{
return mAssociatedEdgeIds[idx];
}
std::size_t Node::getConnectedFaceId(std::size_t idx) const
{
return mAssociatedFaceIds[idx];
}
void Node::setIndex(std::size_t idx)
{
mIndex = idx;
}

69
src/rendering/mesh/Node.h Normal file
View file

@ -0,0 +1,69 @@
#pragma once
#include "Point.h"
#include "Transform.h"
#include <unordered_map>
#include <string>
#include <vector>
class Node
{
public:
enum class State
{
HEALTHY,
DIRTY
};
Node(const Point& p, std::size_t index = 0);
static std::unique_ptr<Node> Create(const Point& p, std::size_t index = 0);
~Node();
void apply(const Transform& transform);
void associateEdge(std::size_t edgeId);
void associateFace(std::size_t faceId);
void addVectorAttribute(const std::string& tag, const std::vector<double>& values);
void clearConnectivity();
std::size_t getIndex() const;
std::size_t getNumConnectedEdges() const;
std::size_t getNumConnectedFaces() const;
std::size_t getConnectedEdgeId(std::size_t idx) const;
std::size_t getConnectedFaceId(std::size_t idx) const;
State getState() const;
const Point& getPoint() const;
std::vector<double> getVectorAttribute(const std::string& tag) const;
bool isCoincident(Node* node) const;
void setState(State state);
void setIndex(std::size_t idx);
void updateIndex(std::size_t index);
private:
std::unordered_map<std::string, std::vector<double> > mVectorAttributes;
std::size_t mIndex{0};
Point mPoint;
std::vector<std::size_t> mAssociatedEdgeIds;
std::vector<std::size_t> mAssociatedFaceIds;
State mState{State::HEALTHY};
};

View file

View file

@ -0,0 +1,8 @@
#pragma once
class Edge;
class QuadFace
{
};

View file

View file

View file

@ -0,0 +1,108 @@
#include "TriFace.h"
#include "Edge.h"
#include "Node.h"
TriFace::TriFace(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id)
: AbstractFace(id),
mEdge0(edge0),
mEdge1(edge1),
mEdge2(edge2)
{
}
std::unique_ptr<TriFace> TriFace::Create(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id)
{
return std::make_unique<TriFace>(edge0, edge1, edge2, id);
}
TriFace::~TriFace()
{
}
std::vector<std::size_t> TriFace::getNodeIds() const
{
return {mEdge0->getNode0Id(), mEdge0->getNode1Id(), mEdge1->getNode1Id()};
}
std::size_t 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;
}
}
std::size_t TriFace::getEdge0Id () const
{
return mEdge0->getId();
}
std::size_t TriFace::getEdge1Id () const
{
return mEdge1->getId();
}
std::size_t TriFace::getEdge2Id () const
{
return mEdge2->getId();
}
std::vector<std::size_t> TriFace::getEdgeIds() const
{
return {mEdge0->getId(), mEdge1->getId(), mEdge2->getId()};
}
void TriFace::associateWidthEdges()
{
mEdge0->associateFace(mId);
mEdge1->associateFace(mId);
mEdge2->associateFace(mId);
}
std::vector<Point> TriFace::getNodeLocations(Orientation orientation) const
{
if (orientation != getOrientation())
{
return { mEdge0->getNode0()->getPoint(), mEdge0->getNode1()->getPoint(), mEdge1->getNode1()->getPoint() };
}
else
{
return { mEdge0->getNode0()->getPoint(), mEdge1->getNode1()->getPoint(), mEdge0->getNode1()->getPoint() };
}
}
Vector TriFace::getNormal() const
{
auto v0 = mEdge0->getNode0()->getPoint().getDelta(mEdge0->getNode1()->getPoint());
auto v1 = mEdge0->getNode0()->getPoint().getDelta(mEdge1->getNode1()->getPoint());
return v0.crossProduct(v1).getNormalized();
}
AbstractFace::Orientation TriFace::getOrientation() const
{
Vector z_norm(0, 0, 1);
if (z_norm.dotProduct(getNormal()) < 0.0)
{
return Orientation::CW;
}
else
{
return Orientation::CCW;
}
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "AbstractFace.h"
#include "Vector.h"
class Edge;
class TriFace : public AbstractFace
{
public:
TriFace(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id=0);
~TriFace();
static std::unique_ptr<TriFace> Create(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id=0);
void associateWidthEdges() override;
std::vector<std::size_t> getNodeIds() const override;
std::size_t getNumNodes() const override;
void replaceEdge(Edge* original, Edge* replacement);
std::size_t getEdge0Id () const;
std::size_t getEdge1Id () const;
std::size_t getEdge2Id () const;
std::vector<std::size_t> getEdgeIds() const override;
std::vector<Point> getNodeLocations(Orientation orientation = Orientation::CCW) const override;
Vector getNormal() const;
AbstractFace::Orientation getOrientation() const;
private:
Edge* mEdge0{nullptr};
Edge* mEdge1{nullptr};
Edge* mEdge2{nullptr};
};

View file

@ -0,0 +1,46 @@
#include "TriMesh.h"
#include "Edge.h"
#include "TriFace.h"
TriMesh::~TriMesh()
{
}
AbstractMesh::MeshType TriMesh::getType() const
{
return AbstractMesh::MeshType::TRI;
}
std::unique_ptr<AbstractMesh> TriMesh::copy() const
{
VecNodes nodes(mNodes.size());
unsigned count = 0;
for (auto& node : mNodes)
{
nodes[count] = std::make_unique<Node>(node->getPoint());
count++;
}
VecEdges edges(mEdges.size());
count = 0;
for (auto& edge : mEdges)
{
edges[count] = std::make_unique<Edge>(nodes[edge->getNode0Id()].get(), nodes[edge->getNode0Id()].get());
count++;
}
VecFaces faces(mFaces.size());
count = 0;
for (auto& face : mFaces)
{
auto ids = face->getEdgeIds();
faces[count] = std::make_unique<TriFace>(edges[ids[0]].get(), edges[ids[1]].get(), edges[ids[2]].get());
count++;
}
auto mesh = std::make_unique<TriMesh>();
mesh->populate(nodes, edges, faces);
return mesh;
}

View file

@ -0,0 +1,15 @@
#pragma once
#include "FaceMesh.h"
class TriMesh : public FaceMesh
{
public:
TriMesh() = default;
~TriMesh();
std::unique_ptr<AbstractMesh> copy() const override;
MeshType getType() const;
};