Commits (17)
......@@ -10,12 +10,13 @@ if(${CMAKE_VERSION} VERSION_GREATER "3.8.0")
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src" FILES ${SOURCE_FILES} ${HEADER_FILES})
endif()
add_library(polymesh STATIC ${SOURCE_FILES} ${HEADER_FILES})
set_property(TARGET polymesh PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(polymesh PUBLIC src/)
if (MSVC)
target_compile_options(polymesh PUBLIC /MP)
else()
target_compile_options(polymesh PRIVATE -Wall -fPIC)
target_compile_options(polymesh PRIVATE -Wall)
target_link_libraries(polymesh PUBLIC -fuse-ld=gold)
endif()
......
......@@ -3,6 +3,8 @@
#include <cstddef>
#include <vector>
#include "fwd.hh"
#include "attributes.hh"
#include "cursors.hh"
#include "detail/unique_array.hh"
......@@ -13,9 +15,6 @@
#include "attribute_collection.hh"
#include "low_level_api.hh"
// alias pm
namespace pm = polymesh;
namespace polymesh
{
/**
......
......@@ -10,6 +10,8 @@
#include "../data/partitioning.hh"
#include "../std/hash.hh"
namespace polymesh
{
/// Optimizes mesh layout for face traversals
......@@ -153,8 +155,8 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
edges.clear();
for (auto const& kvp : cluster_neighbors)
{
auto f0 = face_index(kvp.first / fcnt);
auto f1 = face_index(kvp.first % fcnt);
auto f0 = face_index(int(kvp.first / fcnt));
auto f1 = face_index(int(kvp.first % fcnt));
edges.push_back({kvp.second, {f0, f1}});
}
sort(edges.begin(), edges.end());
......@@ -273,8 +275,8 @@ inline std::vector<int> cache_coherent_vertex_layout(Mesh const& m)
edges.clear();
for (auto const& kvp : cluster_neighbors)
{
auto f0 = vertex_index(kvp.first / vcnt);
auto f1 = vertex_index(kvp.first % vcnt);
auto f0 = vertex_index(int(kvp.first / vcnt));
auto f1 = vertex_index(int(kvp.first % vcnt));
edges.push_back({kvp.second, {f0, f1}});
}
sort(edges.begin(), edges.end());
......
......@@ -203,13 +203,12 @@ Scalar triangle_area(face_handle f, vertex_attribute<Pos3> const& position)
template <class Pos3>
Pos3 triangle_centroid(face_handle f, vertex_attribute<Pos3> const& position)
{
auto z = field3<Pos3>::zero_pos();
auto h = f.any_halfedge();
auto p0 = position[h.vertex_from()] - z;
auto p1 = position[h.vertex_to()] - z;
auto p2 = position[h.next().vertex_to()] - z;
auto p0 = position[h.vertex_from()];
auto p1 = position[h.vertex_to()];
auto p2 = position[h.next().vertex_to()];
return z + (p0 + p1 + p2) / field3<Pos3>::scalar(3);
return (p0 + p1 + p2) / field3<Pos3>::scalar(3);
}
template <class Pos3>
......@@ -278,22 +277,20 @@ Pos3 face_centroid(face_handle f, vertex_attribute<Pos3> const& position)
{
// TODO: make correct for non-convex polygons!
auto z = field3<Pos3>::zero_pos();
auto area = field3<Pos3>::scalar(0);
auto centroid = z;
decltype(Pos3{} + Pos3{}) centroid = field3<Pos3>::zero_pos();
auto h = f.any_halfedge();
auto v0 = h.vertex_from();
auto p0 = v0[position] - z;
auto p0 = v0[position];
auto p_prev = h.vertex_to()[position] - z;
auto p_prev = h.vertex_to()[position];
h = h.next();
do
{
auto p_curr = h.vertex_to()[position] - z;
auto p_curr = h.vertex_to()[position];
auto a = field3<Pos3>::length(field3<Pos3>::cross(p_prev - p0, p_curr - p0));
area += a;
......@@ -394,12 +391,11 @@ Scalar angle_defect(vertex_handle v, vertex_attribute<Pos3> const& position)
template <class Pos3>
Pos3 bary_interpolate(face_handle f, Pos3 bary, vertex_attribute<Pos3> const& position)
{
auto z = field3<Pos3>::zero_pos();
auto h = f.any_halfedge();
auto v0 = h.vertex_to()[position] - z;
auto v1 = h.next().vertex_to()[position] - z;
auto v2 = h.next().next().vertex_to()[position] - z;
return z + v0 * bary[0] + v1 * bary[1] + v2 * bary[2];
auto v0 = h.vertex_to()[position];
auto v1 = h.next().vertex_to()[position];
auto v2 = h.next().next().vertex_to()[position];
return (v0 * bary[0] + v1 * bary[1] + v2 * bary[2]) / field3<Pos3>::scalar(1);
}
template <class Pos3, class Scalar>
......
#pragma once
#include <polymesh/Mesh.hh>
#include <polymesh/detail/delaunay.hh>
// Basic mesh operations, including:
// - elementary subdivision
......@@ -11,6 +12,10 @@ namespace polymesh
/// Given a flat polymesh with convex faces, naively triangulates all faces
void triangulate_naive(Mesh& m);
/// Given a 2d mesh filled with vertices, creates a delauny triangulation
template <class Pos2>
bool add_delauny_triangulation(Mesh& m, vertex_attribute<Pos2> const& position);
/// ======== IMPLEMENTATION ========
inline void triangulate_naive(Mesh& m)
......@@ -32,4 +37,21 @@ inline void triangulate_naive(Mesh& m)
m.faces().add(vs[0], vs[i - 1], vs[i]);
}
}
template <class Pos2>
bool add_delauny_triangulation(Mesh& m, vertex_attribute<Pos2> const& pos)
{
POLYMESH_ASSERT(m.vertices().size() >= 3 && "Mesh must have at least 3 vertices");
auto p = std::vector<float>(pos.size() * 2);
for (auto i = 0u; i < pos.size(); ++i)
{
p[i * 2 + 0] = float(pos[vertex_index(i)][0]);
p[i * 2 + 1] = float(pos[vertex_index(i)][1]);
}
return detail::add_delauny_triangulation_delabella(m, p.data());
}
} // namespace polymesh
......@@ -24,7 +24,7 @@ void print_stats(std::ostream& out, Mesh const& m, vertex_attribute<Vec3> const*
{
auto ln = "\n";
auto to_string = [&](Vec3 const& v) {
auto to_string = [&](auto const& v) {
std::stringstream ss;
ss << "(" << v[0] << ", " << v[1] << ", " << v[2] << ")";
return ss.str();
......
This diff is collapsed.
#pragma once
#include "../Mesh.hh"
namespace polymesh::detail
{
bool add_delauny_triangulation_delabella(Mesh &m, float const *pos);
} // namespace polymesh::detail
......@@ -66,3 +66,6 @@ struct vertex_vertex_ring;
struct halfedge_ring;
}
// alias pm
namespace pm = polymesh;
......@@ -241,14 +241,14 @@ auto smart_range<this_t, ElementT>::avg(FuncT&& f) const -> tmp::decayed_result_
auto it_begin = static_cast<this_t const*>(this)->begin();
auto it_end = static_cast<this_t const*>(this)->end();
POLYMESH_ASSERT(it_begin != it_end && "requires non-empty range");
auto s = f(*it_begin);
decltype(f(*it_begin) + f(*it_begin)) s = f(*it_begin);
auto cnt = 1;
static_assert(tmp::can_divide_by<decltype(s), decltype(cnt)>::value, "Cannot divide sum by an integer. (if glm is used, including <glm/ext.hpp> "
"might help)");
++it_begin;
while (it_begin != it_end)
{
s = detail::helper_add(s, f(*it_begin));
s = s + f(*it_begin);
++cnt;
++it_begin;
}
......@@ -263,7 +263,7 @@ auto smart_range<this_t, ElementT>::weighted_avg(FuncT&& f, WeightT&& w) const -
auto it_end = static_cast<this_t const*>(this)->end();
POLYMESH_ASSERT(it_begin != it_end && "requires non-empty range");
auto e = *it_begin;
auto s = f(e);
decltype(f(e) + f(e)) s = f(e);
auto ws = w(e);
static_assert(tmp::can_divide_by<decltype(s), decltype(ws)>::value, "Cannot divide sum by weight. (if glm is used, including <glm/ext.hpp> might "
"help)");
......@@ -272,7 +272,7 @@ auto smart_range<this_t, ElementT>::weighted_avg(FuncT&& f, WeightT&& w) const -
{
auto ee = *it_begin;
auto ww = w(ee);
s = detail::helper_add(s, f(ee) * ww);
s = s + f(ee) * ww;
ws = ws + ww;
++it_begin;
}
......@@ -316,7 +316,7 @@ template <class this_t, class ElementT>
template <class FuncT>
auto smart_range<this_t, ElementT>::order_statistic(float p, FuncT&& f) const -> tmp::decayed_result_type_of<FuncT, ElementT>
{
auto vals = this->to_vector();
auto vals = this->to_vector(f);
POLYMESH_ASSERT(!vals.empty() && "requires non-empty range");
auto n = (int)(std::roundf(vals.size() * p));
if (n < 0)
......@@ -580,13 +580,12 @@ typename primitive<tag>::handle smart_collection<mesh_ptr, tag, iterator>::rando
{
auto s = primitive<tag>::all_size(*this->m);
POLYMESH_ASSERT(s > 0 && "Cannot chose from empty mesh");
std::uniform_int_distribution<> uniform(0, s - 1);
typename primitive<tag>::handle h = {this->m, typename primitive<tag>::index(uniform(g))};
typename primitive<tag>::handle h = {this->m, typename primitive<tag>::index(int(g() % s))};
if (iterator::is_valid_iterator)
while (h.is_removed())
h = {this->m, typename primitive<tag>::index(uniform(g))};
h = {this->m, typename primitive<tag>::index(int(g() % s))};
return h;
}
......
#pragma once
#include "../Mesh.hh"
namespace polymesh::objects
{
/// Adds a (tessellated) cone to the given mesh
/// qf is called with (v, x, y), with vertex handle v and (x,y) from 0..1 (row-by-row)
/// x is tesselated from 0..1 and called with y = 0 (disk)
/// apex is called with (0, 1)
/// returns the one of the new vertices
/// NOTE: the result is NOT triangulated!
template <class coneF>
auto add_cone(Mesh& m, coneF&& qf, int segments, bool closed = true) -> decltype(qf(vertex_handle{}, float{}, float{}), vertex_handle{});
/// ======== IMPLEMENTATION ========
template <class coneF>
auto add_cone(Mesh& m, coneF&& qf, int segments, bool closed) -> decltype(qf(vertex_handle{}, float{}, float{}), vertex_handle{})
{
POLYMESH_ASSERT(segments > 2);
auto v_top = m.vertices().add();
qf(v_top, 0.0f, 1.0f);
std::vector<vertex_index> v_bot(segments);
for (auto i = 0; i < segments; ++i)
{
auto v = m.vertices().add();
v_bot[i] = v;
qf(v, i / float(segments), 0.0f);
}
for (auto i = 0; i < segments; ++i)
{
auto v01 = v_bot[i].of(m);
auto v11 = v_bot[(i + 1) % segments].of(m);
m.faces().add(v_top, v01, v11);
}
if (closed)
{
reverse(begin(v_bot), end(v_bot));
m.faces().add(v_bot);
}
return v_top;
}
}
#pragma once
#include "../Mesh.hh"
namespace polymesh::objects
{
/// Adds a (tessellated) cylinder to the given mesh
/// qf is called with (v, x, y), with vertex handle v and (x,y) from 0..1 (row-by-row)
/// x is tesselated from 0..1
/// y is either 0 or 1
/// returns the one of the new vertices
/// NOTE: the result is NOT triangulated!
template <class CylinderF>
auto add_cylinder(Mesh& m, CylinderF&& qf, int segments, bool closed = true) -> decltype(qf(vertex_handle{}, float{}, float{}), vertex_handle{});
/// ======== IMPLEMENTATION ========
template <class CylinderF>
auto add_cylinder(Mesh& m, CylinderF&& qf, int segments, bool closed) -> decltype(qf(vertex_handle{}, float{}, float{}), vertex_handle{})
{
POLYMESH_ASSERT(segments > 2);
std::vector<vertex_index> v_top(segments);
std::vector<vertex_index> v_bot(segments);
for (auto i = 0; i < segments; ++i)
{
{
auto v = m.vertices().add();
v_top[i] = v;
qf(v, i / float(segments), 0.0f);
}
{
auto v = m.vertices().add();
v_bot[i] = v;
qf(v, i / float(segments), 1.0f);
}
}
for (auto i = 0; i < segments; ++i)
{
auto v00 = v_top[i].of(m);
auto v10 = v_top[(i + 1) % segments].of(m);
auto v01 = v_bot[i].of(m);
auto v11 = v_bot[(i + 1) % segments].of(m);
m.faces().add(v00, v01, v11, v10);
}
if (closed)
{
m.faces().add(v_top);
reverse(begin(v_bot), end(v_bot));
m.faces().add(v_bot);
}
return v_top[0].of(m);
}
}
#pragma once
#include "../Mesh.hh"
namespace polymesh::objects
{
/// Adds a (tessellated) uv_sphere to the given mesh
/// Top and bottom vertex is shared
/// qf is called with (v, x, y), with vertex handle v and (x,y) from 0..1 (row-by-row)
/// top and bottom are generated with (0,0) and (0,1) respectively
/// returns the one of the new vertices
/// NOTE: the result is NOT triangulated!
template <class SphereF>
auto add_uv_sphere(Mesh& m, SphereF&& qf, int cnt_longitude, int cnt_latitude) -> decltype(qf(vertex_handle{}, float{}, float{}), vertex_handle{});
/// ======== IMPLEMENTATION ========
template <class SphereF>
auto add_uv_sphere(Mesh& m, SphereF&& qf, int cnt_longitude, int cnt_latitude) -> decltype(qf(vertex_handle{}, float{}, float{}), vertex_handle{})
{
// TODO: this is topologically not exactly correct I think
POLYMESH_ASSERT(cnt_latitude > 1 && cnt_longitude > 2);
std::vector<vertex_index> verts((cnt_latitude + 1) * (cnt_longitude + 1));
auto v_top = m.vertices().add();
auto v_bot = m.vertices().add();
qf(v_top, 0.0f, 0.0f);
qf(v_bot, 0.0f, 1.0f);
auto i = 0;
for (auto y = 0; y <= cnt_latitude; ++y)
for (auto x = 0; x <= cnt_longitude; ++x)
{
if (y == 0)
{
verts[i] = v_top;
}
else if (y == cnt_latitude)
{
verts[i] = v_bot;
}
else
{
auto pu = x / float(cnt_longitude);
auto pv = y / float(cnt_latitude);
auto v = m.vertices().add();
verts[i] = v;
qf(v, pu, pv);
}
++i;
}
for (auto y = 0; y < cnt_latitude; ++y)
for (auto x = 0; x <= cnt_longitude; ++x)
{
auto v00 = verts[(y + 0) * (cnt_longitude + 1) + (x + 0) % (cnt_longitude + 1)].of(m);
auto v10 = verts[(y + 0) * (cnt_longitude + 1) + (x + 1) % (cnt_longitude + 1)].of(m);
auto v01 = verts[(y + 1) * (cnt_longitude + 1) + (x + 0) % (cnt_longitude + 1)].of(m);
auto v11 = verts[(y + 1) * (cnt_longitude + 1) + (x + 1) % (cnt_longitude + 1)].of(m);
if (v00 == v10)
m.faces().add(v00, v01, v11);
else if (v01 == v11)
m.faces().add(v00, v11, v10);
else
m.faces().add(v00, v01, v11, v10);
}
return v_top;
}
}
......@@ -2,7 +2,10 @@
// Includes all polymesh features
#include "Mesh.hh"
#include <polymesh/Mesh.hh>
#include "objects.hh"
#include "formats.hh"
#include <polymesh/algorithms/optimization.hh>
#include <polymesh/algorithms/properties.hh>
#include <polymesh/formats.hh>
#include <polymesh/objects.hh>