Skip to content
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
6 changes: 6 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,9 @@ target_link_libraries(threewindows OpenGL::GL glfw Freetype::Freetype)
add_executable(rod rod.cpp)
target_link_libraries(rod OpenGL::GL glfw Freetype::Freetype)

add_executable(rod_with_normals rod_with_normals.cpp)
target_link_libraries(rod_with_normals OpenGL::GL glfw Freetype::Freetype)

add_executable(rod_pan rod_pan.cpp)
target_link_libraries(rod_pan OpenGL::GL glfw Freetype::Freetype)

Expand Down Expand Up @@ -367,6 +370,9 @@ if(NOT APPLE)
add_executable(geodesic geodesic.cpp)
target_link_libraries(geodesic OpenGL::GL glfw Freetype::Freetype)

add_executable(geodesic_with_normals geodesic_with_normals.cpp)
target_link_libraries(geodesic_with_normals OpenGL::GL glfw Freetype::Freetype)

add_executable(geodesic_ce geodesic_ce.cpp)
target_link_libraries(geodesic_ce OpenGL::GL glfw Freetype::Freetype)
endif()
Expand Down
72 changes: 72 additions & 0 deletions examples/geodesic_with_normals.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Visualize a sequence of icosahedral geodesics, showing their normals
*/

#include <iostream>
#include <fstream>
#include <cmath>
#include <array>
#include <stdexcept>
#include <string>

#include <sm/vec>

#include <mplot/Visual.h>
#include <mplot/ColourMap.h>
#include <mplot/GeodesicVisual.h>
#include <mplot/NormalsVisual.h>

int main()
{
int rtn = -1;

mplot::Visual v(1024, 768, "Geodesic Polyhedra with normals");
v.showCoordArrows (true);
// Set the Visual to rotate about the nearest VisualModel (Change at runtime with Ctrl-k)
v.rotateAboutNearest (true);
// In this example, use the 'rotate about a scene vertical axis' mode
v.rotateAboutVertical (true);

try {
sm::vec<float, 3> offset = {};
sm::vec<float, 3> step = { 2.2f };

mplot::ColourMap<float> cm (mplot::ColourMapType::Jet);
int imax = 4;
for (int i = 0; i < imax; ++i) {
auto cl = cm.convert (i / static_cast<float>(imax - 1));
auto gv1 = std::make_unique<mplot::GeodesicVisual<float>> (offset + step * i, 0.9f);
v.bindmodel (gv1);
gv1->iterations = i;
std::string lbl = std::string("iterations = ") + std::to_string(i);
gv1->addLabel (lbl, {0, -1, 0}, mplot::TextFeatures(0.06f));
gv1->cm.setType (mplot::ColourMapType::Jet);
gv1->colour_bb = cl;
gv1->finalize();

// re-colour after construction
auto gv1p = v.addVisualModel (gv1);
float imax_mult = 1.0f / static_cast<float>(imax);
// sequential colouring:
size_t sz1 = gv1p->data.size();
gv1p->data.linspace (0.0f, 1+i * imax_mult, sz1);
gv1p->reinitColours();

gv1p->vertex_postprocess(); // creates the triangles and normals required for NormalsVisual

// Create an associate normals model
auto nrm = std::make_unique<mplot::NormalsVisual<>> (gv1p);
v.bindmodel (nrm);
nrm->finalize();
v.addVisualModel (nrm);
}

v.keepOpen();

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

return rtn;
}
72 changes: 72 additions & 0 deletions examples/rod_with_normals.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Visualize a Rod
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <array>
#include <stdexcept>
#include <string>

#include <sm/vec>

#include <mplot/Visual.h>
#include <mplot/ColourMap.h>
#include <mplot/RodVisual.h>
#include <mplot/NormalsVisual.h>

int main()
{
int rtn = -1;

mplot::Visual v(1024, 768, "Visualization");
v.zNear = 0.001;
//v.showCoordArrows (true);
//v.coordArrowsInScene (true);
// For a white background:
v.backgroundWhite();
// Switch on a mix of diffuse/ambient lighting
v.lightingEffects(true);

try {
constexpr sm::vec<float, 3> colour1 = { 1.0, 0.0, 0.0 };
sm::vec<float, 3> offset = { 0.0, 0.0, 0.0 };
sm::vec<float, 3> start = { 0, 0, 0 };
sm::vec<float, 3> end = { 0.25, 0, 0 };
auto rvm = std::make_unique<mplot::RodVisual<>> (offset, start, end, 0.1f, colour1, colour1);
v.bindmodel (rvm);
rvm->use_oriented_tube = false;
rvm->finalize();
auto rvmp1 = v.addVisualModel (rvm);
rvmp1->vertex_postprocess();

auto nrm = std::make_unique<mplot::NormalsVisual<>> (rvmp1);
v.bindmodel (nrm);
nrm->finalize();
v.addVisualModel (nrm);

constexpr sm::vec<float, 3> colour2 = { 0.0, 0.9, 0.4 };
sm::vec<float, 3> start2 = { -0.1, 0.2, 0.6 };
sm::vec<float, 3> end2 = { 0.2, 0.4, 0.6 };
// You can reuse the unique_ptr rvm, once you've transferred ownership with v.addVisualModel (rvm)
rvm = std::make_unique<mplot::RodVisual<>>(offset, start2, end2, 0.05f, colour2);
v.bindmodel (rvm);
rvm->finalize();
auto rvmp2 = v.addVisualModel (rvm);
rvmp2->vertex_postprocess();

// Create an associate normals model
nrm = std::make_unique<mplot::NormalsVisual<>> (rvmp2);
v.bindmodel (nrm);
nrm->finalize();
v.addVisualModel (nrm);

v.keepOpen();

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

return rtn;
}
2 changes: 1 addition & 1 deletion maths
1 change: 1 addition & 0 deletions mplot/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ install(
HSVWheelVisual.h
IcosaVisual.h
LengthscaleVisual.h
NormalsVisual.h
PointRowsMeshVisual.h
PointRowsVisual.h
PolarVisual.h
Expand Down
90 changes: 90 additions & 0 deletions mplot/NormalsVisual.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#pragma once

/*!
* \file Declares NormalsVisual to visualize the normals of another VisualModel
*/

#include <array>
#include <sm/vec>
#include <mplot/colour.h>
#include <mplot/VisualModel.h>

namespace mplot {

//! A class to visualize normals for another model
template <int glver = mplot::gl::version_4_1>
class NormalsVisual : public VisualModel<glver>
{
public:
NormalsVisual(mplot::VisualModel<glver>* _mymodel)
{
this->mymodel = _mymodel;
this->viewmatrix = _mymodel->getViewMatrix();
}

void initializeVertices()
{
if (mymodel == nullptr) {
std::cout << "NormalsVisual: I have no model; returning\n";
return;
}

const float cone_r = this->thickness * this->scale_factor * 2.0f;
const float tube_r = this->thickness * this->scale_factor;
// Copy data out of my model...
std::vector<float> mymodelPositions = mymodel->getVertexPositions();
std::vector<float> mymodelNormals = mymodel->getVertexNormals();
std::vector<float> mymodelColors = {};
if (!singlecolour) { mymodelColors = mymodel->getVertexColors(); }
// And interpret it
auto vp = reinterpret_cast<const std::vector<sm::vec<float, 3>>*>(&mymodelPositions);
auto vn = reinterpret_cast<const std::vector<sm::vec<float, 3>>*>(&mymodelNormals);
auto vc = reinterpret_cast<const std::vector<std::array<float, 3>>*>(&mymodelColors);

for (uint32_t ii = 0; ii < vn->size(); ++ii) {
// (*vp)[ii] is position, (*vn)[ii] is normal
std::array<float, 3> _clr = clr;
if (!singlecolour) { _clr = (*vc)[ii]; }
this->computeArrow ((*vp)[ii], ((*vp)[ii] + (*vn)[ii] * this->scale_factor),
_clr, tube_r, this->arrowhead_prop, cone_r, this->shapesides);
}

std::array<uint32_t, 3> ti = {};
sm::vec<float, 3> nv = {};
sm::vec<float, 3> nvc = {};
sm::vec<float, 3> nvd = {};
sm::vec<float, 3> pos = {};
// We also have vp1 (public) and triangles (also public)
for (auto t : mymodel->triangles) {
std::tie(ti, nv, nvc, nvd) = t;
// Plot tn at mean location of ti
pos = (mymodel->vp1[ti[0]] + mymodel->vp1[ti[1]] + mymodel->vp1[ti[2]]) / 3.0f;
// Mesh triangle normals
this->computeArrow (pos, (pos + nv * this->scale_factor),
clr, tube_r, this->arrowhead_prop, cone_r, this->shapesides);
// Computed triangle normals
this->computeArrow (pos, (pos + nvc * this->scale_factor),
clrnc, tube_r, this->arrowhead_prop, cone_r, this->shapesides);
this->computeArrow (pos, (pos + nvd * scale_factor),
clrnd, tube_r, this->arrowhead_prop, cone_r, this->shapesides);
}
};

// The model for which we will plot normal vectors
mplot::VisualModel<glver>* mymodel = nullptr;
// How many sides to each normal vector
int shapesides = 12;
// thickness for the normal vectors
float thickness = 0.025f;
// What proportion of the arrow length should the arrowhead length be?
float arrowhead_prop = 0.25f;
// How much to linearly scale the size of the vector
float scale_factor = 0.1f;
// Vector single colour
bool singlecolour = false;
std::array<float, 3> clr = mplot::colour::grey20;
std::array<float, 3> clrnc = mplot::colour::grey60; // computed norm
std::array<float, 3> clrnd = mplot::colour::grey90; // computed norm
};

} // namespace mplot
12 changes: 8 additions & 4 deletions mplot/RodVisual.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ namespace mplot {
}
}

void update (const sm::vec<float, 3>& s, const sm::vec<float, 3>& e)
template <std::size_t N = 3> requires (N == 3) || (N == 4)
void update (const sm::vec<float, N>& s, const sm::vec<float, N>& e)
{
this->start_coord = s;
this->end_coord = e;
for (std::size_t i = 0; i < 3; ++i) {
this->start_coord[i] = s[i];
this->end_coord[i] = e[i];
}
this->reinit();
}

Expand All @@ -81,7 +84,8 @@ namespace mplot {
float radius = 1.0f;
//! If true, use face_uz and face_uy to draw the tube, else get a square-ended tube
bool use_oriented_tube = true;
//! Face directions for the oriented tube
//! Face directions for the oriented tube's *end* cap. Choose carefully so that face_uy ^
//! face_uz gives the normal for the end cap.
sm::vec<float, 3> face_uy = sm::vec<float, 3>::uy();
sm::vec<float, 3> face_uz = sm::vec<float, 3>::uz();

Expand Down
Loading