Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/constexpr geodesics vec #187

Merged
merged 6 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ if(wxWidgets_FOUND)
endif()
endif()

# Make it possible to compile complex constexpr functions
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fconstexpr-ops-limit=5000000000")

# rapidxml is bundled in the source, but its headers will be installed in ${CMAKE_INSTALL_PREFIX}/morph/, and they're symlinked in ${PROJECT_SOURCE_DIR}/morph
#include_directories("${PROJECT_SOURCE_DIR}/include/rapidxml-1.13")

Expand Down
13 changes: 12 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ if(USE_GLEW)
endif()

add_executable(scatter scatter.cpp)
set_property(TARGET scatter PROPERTY CXX_STANDARD 20)
target_link_libraries(scatter OpenGL::GL glfw Freetype::Freetype)
if(USE_GLEW)
target_link_libraries(scatter GLEW::GLEW)
Expand Down Expand Up @@ -326,15 +327,25 @@ if(USE_GLEW)
target_link_libraries(icosahedron GLEW::GLEW)
endif()

# Need C++20 for the way I use constexpr here so that it is legal to
# have "a definition of a variable for which no initialization is
# performed".
add_executable(testce testce.cpp)
set_property(TARGET testce PROPERTY CXX_STANDARD 23)
set_property(TARGET testce PROPERTY CXX_STANDARD 20)

add_executable(geodesic geodesic.cpp)
target_link_libraries(geodesic OpenGL::GL glfw Freetype::Freetype)
if(USE_GLEW)
target_link_libraries(geodesic GLEW::GLEW)
endif()

add_executable(geodesic_ce geodesic_ce.cpp)
set_property(TARGET geodesic_ce PROPERTY CXX_STANDARD 20)
target_link_libraries(geodesic_ce OpenGL::GL glfw Freetype::Freetype)
if(USE_GLEW)
target_link_libraries(geodesic_ce GLEW::GLEW)
endif()

add_executable(tri tri.cpp)
target_link_libraries(tri OpenGL::GL glfw Freetype::Freetype)
if(USE_GLEW)
Expand Down
18 changes: 4 additions & 14 deletions examples/geodesic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ int main()
{
int rtn = -1;

morph::Visual v(1024, 768, "Geodesic Polyhedron");
morph::Visual v(1024, 768, "Geodesic Polyhedra (ordered vertices/faces)");
v.showCoordArrows = true;

try {
Expand All @@ -31,25 +31,15 @@ int main()
std::string lbl = std::string("iterations = ") + std::to_string(i);
gv1->addLabel (lbl, {0, -1, 0}, morph::TextFeatures(0.06f));
gv1->cm.setType (morph::ColourMapType::Jet);
//gv1->colourScale.do_autoscale = false;
//gv1->colourScale.compute_autoscale (0.0f, 2.0f);
gv1->finalize();
#if 0 // no re-colouring
v.addVisualModel (gv1);
#else // re-colour after construction
auto gv1p = v.addVisualModel (gv1);

// re-colour after construction
auto gv1p = v.addVisualModel (gv1);
float imax_mult = 1.0f / static_cast<float>(imax);
# if 0 // Funky pattern colouring
for (unsigned int j = 0; j < gv1p->data.size(); ++j) {
gv1p->data[j] = j%3 ? 0 : (1+i) * imax_mult;
}
# else // sequential colouring
// sequential colouring:
size_t sz1 = gv1p->data.size();
gv1p->data.linspace (0.0f, 1+i * imax_mult, sz1);
# endif
gv1p->updateColours();
#endif
}

v.keepOpen();
Expand Down
64 changes: 64 additions & 0 deletions examples/geodesic_ce.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Visualize an Icosahedron using the Geodesic Visual that co-ops the unordered
* constexpr geodesic function.
*/
#include <morph/Visual.h>
#include <morph/ColourMap.h>
#include <morph/GeodesicVisualCE.h>
#include <morph/vec.h>
#include <iostream>
#include <fstream>
#include <cmath>
#include <array>
#include <stdexcept>
#include <string>

int main()
{
int rtn = -1;

morph::Visual v(1024, 768, "(constexpr) Geodesic Polyhedra");
v.showCoordArrows = true;
v.lightingEffects (true);

try {
morph::vec<float, 3> offset = { 0.0, 0.0, 0.0 };
morph::vec<float, 3> step = { 2.2, 0.0, 0.0 };

auto gv1 = std::make_unique<morph::GeodesicVisualCE<float, 0>> (offset + step * 0, 0.9f);
v.bindmodel (gv1);
std::string lbl = std::string("iterations = 0");
gv1->addLabel (lbl, {0, -1, 0}, morph::TextFeatures(0.06f));
gv1->finalize();
v.addVisualModel (gv1);

auto gv2 = std::make_unique<morph::GeodesicVisualCE<float, 1>> (offset + step * 1, 0.9f);
v.bindmodel (gv2);
lbl = std::string("iterations = 1");
gv2->addLabel (lbl, {0, -1, 0}, morph::TextFeatures(0.06f));
gv2->finalize();
v.addVisualModel (gv2);

auto gv3 = std::make_unique<morph::GeodesicVisualCE<float, 2>> (offset + step * 2, 0.9f);
v.bindmodel (gv3);
lbl = std::string("iterations = 2");
gv3->addLabel (lbl, {0, -1, 0}, morph::TextFeatures(0.06f));
gv3->finalize();
v.addVisualModel (gv3);

auto gv4 = std::make_unique<morph::GeodesicVisualCE<float, 3>> (offset + step * 3, 0.9f);
v.bindmodel (gv4);
lbl = std::string("iterations = 4");
gv4->addLabel (lbl, {0, -1, 0}, morph::TextFeatures(0.06f));
gv4->finalize();
v.addVisualModel (gv4);

v.keepOpen();

} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
rtn = -1;
}

return rtn;
}
1 change: 1 addition & 0 deletions examples/scatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ int main()
while (v.readyToFinish == false) {
v.waitevents (0.018);
v.render();
v.readyToFinish = true; // debug/profile
}

} catch (const std::exception& e) {
Expand Down
6 changes: 5 additions & 1 deletion examples/testce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ int main()
{
// Note that we force the compile time evaluation of geometry_ce::icosahedron by returning a constexpr object
constexpr morph::geometry_ce::polyhedron<double, 12, 20> ico = morph::geometry_ce::icosahedron<double>();
std::cout << "constexpr vertices:\n" << ico.vertices.str() << std::endl;
std::cout << "constexpr 1st vertices:\n" << static_cast<morph::vec<double, 3>>(ico.vertices[0]) << std::endl;

constexpr
auto geo = morph::geometry_ce::make_icosahedral_geodesic<double, 3>();
std::cout << "constexpr geo.vertices: " << static_cast<morph::vec<double, 3>>(geo.poly.vertices[0]).str() << std::endl;
return 0;
}
68 changes: 68 additions & 0 deletions morph/GeodesicVisualCE.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#include <morph/vec.h>
#include <morph/VisualModel.h>
#include <morph/mathconst.h>
#include <morph/Scale.h>
#include <morph/ColourMap.h>
#include <array>

namespace morph {

/*!
* This class creates the vertices for an geodesic polyhedron in a 3D scene using
* the constexpr function.
*
* \tparam T the type for the data to be visualized as face (or maybe vertex) colours
*
* \tparam glver The usual OpenGL version code; match this to everything else in
* your program.
*/
template<typename T, int iterations, int glver = morph::gl::version_4_1>
class GeodesicVisualCE : public VisualModel<glver>
{
public:
GeodesicVisualCE() { this->init ({0.0, 0.0, 0.0}, 1.0f); }

//! Initialise with offset, start and end coordinates, radius and a single colour.
GeodesicVisualCE(const vec<float, 3> _offset, const float _radius)
{
this->init (_offset, _radius);
}

~GeodesicVisualCE () {}

void init (const vec<float, 3> _offset, const float _radius)
{
// Set up...
this->mv_offset = _offset;
this->viewmatrix.translate (this->mv_offset);
this->radius = _radius;
}

//! Initialize vertex buffer objects and vertex array object.
void initializeVertices()
{
this->vertexPositions.clear();
this->vertexNormals.clear();
this->vertexColors.clear();
this->indices.clear();

if (iterations > 5) {
// Note odd necessity to stick in the 'template' keyword after this->
this->template computeSphereGeoFast<double, iterations> (this->idx, morph::vec<float, 3>({0,0,0}),
this->colour, this->radius);
} else {
// computeSphereGeo F defaults to float
this->template computeSphereGeoFast<float, iterations> (this->idx, morph::vec<float, 3>({0,0,0}),
this->colour, this->radius);
}
}

//! The radius of the geodesic
float radius = 1.0f;
//! Fixed colour.
std::array<float, 3> colour = { 0.2f, 0.1f, 0.7f };
};

} // namespace morph
14 changes: 5 additions & 9 deletions morph/ScatterVisual.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,14 @@ namespace morph {
if (this->sizeFactor == Flt{0}) {
if constexpr (draw_spheres_as_geodesics) {
// Slower than regular computeSphere(). 2 iterations gives 320 faces
this->computeSphereGeo (this->idx, (*this->dataCoords)[i], clr, this->radiusFixed, 2);
this->template computeSphereGeoFast<float, 3> (this->idx, (*this->dataCoords)[i], clr, this->radiusFixed);
} else {
// (16+2) * 20 gives 360 faces
this->computeSphere (this->idx, (*this->dataCoords)[i], clr, this->radiusFixed, 16, 20);
}
} else {
if constexpr (draw_spheres_as_geodesics) {
this->computeSphereGeo (this->idx, (*this->dataCoords)[i], clr, dcopy[i]*this->sizeFactor, 2);
this->template computeSphereGeoFast<float, 3> (this->idx, (*this->dataCoords)[i], clr, dcopy[i]*this->sizeFactor);
} else {
this->computeSphere (this->idx, (*this->dataCoords)[i], clr, dcopy[i]*this->sizeFactor, 16, 20);
}
Expand All @@ -134,13 +134,9 @@ namespace morph {
}
}

// I've tested geodesic sphere drawing code, but it probably isn't yet suitable
// for use where many spheres are repeatedly computed (the base geodesic is
// repeatedly computed, which is a waste of cycles). In fact, the overall
// approach here, where each sphere model is repeatedly re-computed for each
// point is probably about the least performant way you could draw a bunch of
// spheres in OpenGL! However, using geodesic code is noticably slower even than
// the regular VisualModel::computeSphere()
// The constexpr, unordered geodesic code is no slower than the regular
// VisualModel::computeSphere(), but leave this off for now (if true, C++-20 is
// required)
static constexpr bool draw_spheres_as_geodesics = false;

//! Set this->radiusFixed, then re-compute vertices.
Expand Down
34 changes: 34 additions & 0 deletions morph/VisualModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,40 @@ namespace morph {
return gi.n_faces;
}

//! Fast computeSphereGeo, which uses constexpr make_icosahedral_geodesic. The
//! resulting vertices and faces are NOT in any kind of order, but ok for
//! plotting, e.g. scatter graph spheres.
template<typename F=float, int iterations = 2>
int computeSphereGeoFast (GLuint& idx, vec<float> so, std::array<float, 3> sc,
float r = 1.0f)
{
// test if type F is float
if constexpr (std::is_same<std::decay_t<F>, float>::value == true) {
static_assert (iterations <= 5, "computeSphereGeoFast: For iterations > 5, F needs to be double precision");
} else {
static_assert (iterations <= 10, "computeSphereGeoFast: This is an abitrary iterations limit (10 gives 20971520 faces)");
}
// Note that we need double precision to compute higher iterations of the geodesic (iterations > 5)
constexpr morph::geometry_ce::icosahedral_geodesic<F, iterations> geo = morph::geometry_ce::make_icosahedral_geodesic<F, iterations>();

// Now essentially copy geo into vertex buffers
for (auto v : geo.poly.vertices) {
this->vertex_push (v.as_float() * r + so, this->vertexPositions);
this->vertex_push (v.as_float(), this->vertexNormals);
this->vertex_push (sc, this->vertexColors);
}
for (auto f : geo.poly.faces) {
this->indices.push_back (idx + f[0]);
this->indices.push_back (idx + f[1]);
this->indices.push_back (idx + f[2]);
}
// idx is the *vertex index* and should be incremented by the number of vertices in the polyhedron
int n_verts = static_cast<int>(geo.poly.vertices.size());
idx += n_verts;

return n_verts;
}

/*!
* Sphere, 1 colour version.
*
Expand Down
Loading