Archive for December, 2016

Plot a bunch of edges in matlab with per-edge color data

Monday, December 19th, 2016

Suppose you have a list of vertices V (#V by dim) and a list of edges E (#E by 2) indexing V, then one hacky to draw all of these edges with per-vertex color data DV (#V by 1) is to use trisurf:

trisurf(E(:,[1 1 2]),V(:,1),V(:,2),V(:,3),'CData',DV,'EdgeColor','interp');


However, if you try to use this trick to draw per-edge color data DE (#E by 1), you’ll find that 'EdgeColor','flat' does not work. Instead you can explode you compact “mesh” of edges into individual edges and repeat you edge color data for each edge point:

trisurf(reshape([1:size(E,1),1:size(E,1)*2],[],3),V(E(:),1),V(E(:),2),V(E(:),3),'CData',repmat(DE,2,1),'EdgeColor','interp');


Command line program to view 3d meshes from files and piped input

Thursday, December 15th, 2016

Here’s a little C++ program to directly render meshes in 3D viewer from the command line.

This let’s you write little test programs without worrying about linking to a 3D viewer. You just need to output a mesh in a standard format. For example, here’s a tiny program that outputs a cube in an .off format:

#include <igl/read_triangle_mesh.h>
#include <Eigen/Core>
#include <iostream>

int main(int argc, char * argv[])
{
using namespace Eigen;
MatrixXd V(8,3);
MatrixXi Q(6,4);
V<<
0,0,1,
0,1,1,
1,1,1,
1,0,1,
0,0,0,
0,1,0,
1,1,0,
1,0,0;
Q<<
3,2,1,0,
0,1,5,4,
6,5,1,2,
3,7,6,2,
4,7,3,0,
4,5,6,7;
std::cout<<
"OFF\n"<<V.rows()<<" "<<Q.rows()<<" 0\n"<<
V.format(IOFormat(FullPrecision,DontAlignCols," ","\n","","","","\n"))<<
(Q.array()).format(IOFormat(FullPrecision,DontAlignCols," ","\n","4 ","","","\n"));
return EXIT_SUCCESS;
}


Compile this into cube_off then issue:

./cube_off | view mesh


**Update: ** And here’s a funny, little one-liner you can call from matlab to display a mesh via the .obj format:

system(sprintf('echo \"%s%s\" | /usr/local/bin/viewmesh',sprintf('v %0.17f %0.17f %0.17f\n',V'),sprintf('f %d %d %d\n',F')))


Sunday, December 4th, 2016

Here’s a minimal example showing how to launch background computation threads for each mesh in a list of meshes. Meanwhile the main thread runs a mesh viewer with all meshes concatenated into one huge multi-component mesh. Whenever a computation thread signals that an update to the mesh needs to be made, the main thread will re-concatenate the meshes and update the viewer. In this example, the “computation” is determining how much to move a clock “hand” (random mesh).

Here’s the program running on the cow, cheburashka and knight models:

// Tiny example to demonstrate spawning a background computation thread for
// each mesh in a list and update the viewer when computation results in a
// change to (one of) the meshes.
//
// In this example, three meshes are read in and interpreted as "hands" of a
// clock. The "computation" is just a busy-wait until the hand should move
// (after one second, one minute, one hour). This is, of course, a terrible way
// to implement a clock.
//
// ./test libigl/tutorial/shared/{cow.off,cheburashka.off,decimated-knight.off}
#include <igl/point_mesh_squared_distance.h>
#include <igl/combine.h>
#include <igl/viewer/Viewer.h>
#include <Eigen/Geometry>
int main(int argc, char * argv[])
{
using namespace std;
using namespace Eigen;
// Read in k meshes (<=3 will be used)
std::vector<Eigen::MatrixXd> VV(argc-1);
std::vector<Eigen::MatrixXi> FF(argc-1);
for(int i = 1;i<argc;i++)
{
VV[i-1].col(0).array() -= VV[i-1].col(0).mean();
VV[i-1].col(1).array() -= VV[i-1].col(1).minCoeff();
}
bool continue_computing = true;
// Flag to redraw and mutex to guard it
bool redraw = false;
std::mutex m;
// After waiting tic seconds, rotate V by deg degrees
const auto rot = [&continue_computing,&redraw,&m](
const double tic, const double deg, Eigen::MatrixXd& V)
{
while(continue_computing)
{
// Let's watch this at 500x: Real clocks are slow.
V *= Eigen::AngleAxisd(deg/180.*igl::PI,
Eigen::Vector3d(0,0,1)).toRotationMatrix();
{
std::lock_guard<std::mutex> lock(m);
redraw = true;
}
}
};
// Launch background "computation" threads for each "hand" of the clock
// std::ref is important, otherwise std::bind passes by copy
switch(VV.size())
{
case 3:
case 2:
case 1:
default: break;
}
igl::viewer::Viewer v;
v.data.clear();
// Helper function to view k meshes
const auto & set_meshes = [](
const std::vector<Eigen::MatrixXd> & VV,
const std::vector<Eigen::MatrixXi> & FF,
igl::viewer::Viewer & v)
{
Eigen::MatrixXd V;
Eigen::MatrixXi F;
igl::combine(VV,FF,V,F);
v.data.set_mesh(V,F);
};
set_meshes(VV,FF,v);
// Continuous draw loop. TODO: trigger draws using glfwPostEmptyEvent
v.core.is_animating = true;
// Before every draw check if the meshes have changed.
v.callback_pre_draw =
[&redraw,&m,&VV,&FF,&set_meshes](igl::viewer::Viewer & v)->bool
{
if(redraw)
{
set_meshes(VV,FF,v);
{
std::lock_guard<std::mutex> lock(m);
redraw = false;
}
}
return false;
};
v.launch();
// Tell computation threads to stop
continue_computing = false;