Latest Posts from Saurabh Garg


Computing edge length of all half-edges in CGAL::Polyhedron_3

In this post I will show how to compute edge length for a half-edge in CGAL::Polyhedron_3.

ComputeEdgeLength() Functor

ComputeEdgeLength() is a thread-safe functor for computing the edge length of a given half-edge in a CGAL::Polyhedron_3.

#ifndef _SMESHLIB_OPERATIONS_COMPUTEEDGELENGTH_H_
#define _SMESHLIB_OPERATIONS_COMPUTEEDGELENGTH_H_

namespace SMeshLib   {
namespace Operations {
;

// The ComputeEdgeLength is a functor (delegate) to compute the length of a halfedge in CGAL::Polyhdeon_3.
// ComputeEdgeLength is thread-safe.
// TPolyhedron is a type of CGAL::Polyhdeon_3.
template<class TPolyhedron>
struct ComputeEdgeLength
{
public:
	
	// Redefine types from TPolyhedron for convenience.
	typedef typename TPolyhedron::Point_3  Point3;
	typedef typename TPolyhedron::Halfedge Halfedge;
	
	// Return type of operator() required by QtConcurrent.
	typedef double result_type;
	
public:
	
	// Compute edge length of a given half edge.
	inline double operator () (const Halfedge& h)
	{
		const Point3& p = h.prev()->vertex()->point();
		const Point3& q = h.vertex()->point();
		return CGAL::sqrt(CGAL::squared_distance(p, q));
	}
};

};	// End namespace Operations.
};	// End namespace SMeshLib.

#endif // _SMESHLIB_OPERATIONS_COMPUTEEDGELENGTH_H_
Using ComputeEdgeLength() Functor

Edge length of a half-edge h can be computed as double length = ComputeEdgeLength(h);.

For most purposes, it is better to compute length of all half-edges once and cache them for later use. It is best to store the results in an associative container which associates the half-edge handle with the edge length. In the following example, I use PropertyMap which is a wrapper for std::set.

#include "ImportOBJ.h"
#include "ComputeEdgeLength.h"
#include "PropertyMap.h"
#include "CGAL/Simple_cartesian.h"
#include "CGAL/Polyhedron_items_3.h"
#include "CGAL/HalfedgeDS_list.h"
#include "CGAL/Polyhedron_3.h"

typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel, 
	                   CGAL::Polyhedron_items_3, 
			   CGAL::HalfedgeDS_list> CgalPolyhedron;

// Compute the length of all half-edges in a CGAL::Polyhedron_3 and stores it 
// in edgeLengths.
// TPolyhedron is a type of CGAL::Polyhedron_3.
// TEdgeLengths is a property map which associates the edge length to half-edge 
// in the CGAL::Polyhdeon_3.
template<class TPolyhedron, class TEdgeLengths>
void computeEdgeLengths(const TPolyhedron& polyhedron, TEdgeLengths* edgeLengths)
{
	if(edgeLengths == 0)
	{
		return;
	}
	
	typename TPolyhedron::Halfedge_const_iterator _begin = polyhedron.halfedges_begin();
	typename TPolyhedron::Halfedge_const_iterator _end   = polyhedron.halfedges_end();

	SMeshLib::Operations::ComputeEdgeLength<TPolyhedron> _computeEdgeLength;
	CGAL_For_all(_begin, _end)
	{
		edgeLengths->setValue(&*_begin, _computeEdgeLength(*_begin));
	}
}

void testComputeEdgeLength()
{
	CgalPolyhedron _poly;
	SMeshLib::IO::importOBJ("Venus.obj", &_poly);
	
	typedef SMeshLib::PropertyMap<const CgalPolyhedron::Halfedge*, double> EdgeLengthPM;
	EdgeLengthPM _edgeLengths;
	computeEdgeLengths(_poly, &_edgeLengths);
}
Downloads

ImportOBJ.h
PropertyMap.h
ComputeEdgeLength.h
TestComputeEdgeLength.cpp
Venus.obj
ImportOBJ.zip


Twenty years from now you will be more disappointed by the things you didn’t do than by the ones you did do. So throw off the bowlines, sail away from the safe harbor. Catch the trade winds in your sails. Explore. Dream. Discover.

Mark Twain

Wavefront OBJ reader for building CGAL::Polyhedron_3

CGAL provides high quality generic half-edge data structure for representing polyhedral surfaces as well as many algorithms for mesh processing. However, CGAL doesn’t have any in-build support for building a polyhedron from Wavefront OBJ or PLY file. The following code is a basic OBJ file loader which reads vertex coordinates and faces (can be polygons) from OBJ file. Note that it doesn’t read vertex normals, face normals, or texture coordinates. Code is well commented and should be fairly obvious.

importOBJ() Function
#ifndef _SMESHLIB_IO_IMPORTOBJ_H_
#define _SMESHLIB_IO_IMPORTOBJ_H_

#include "CGAL/Polyhedron_incremental_builder_3.h"
#include "CGAL/Modifier_base.h"
#include "CGAL/exceptions.h"
#include <string>
#include <fstream>
#include <exception>

namespace SMeshLib {
namespace IO       {
;

// The BuildCgalPolyhedronFromObj class builds a CGAL::Polyhedron_3 from Wavefront OBJ file.
// This is very simple reader and only reads vertex coordinates and vertex index for faces.
// Faces can be polygons and doesn't have to be triangles.
template<class HDS>
class BuildCgalPolyhedronFromObj : public CGAL::Modifier_base<HDS>
{
public:
	
	BuildCgalPolyhedronFromObj(const std::string& fileName) : mFileName(fileName) {}
	
	void operator() (HDS& hds)
	{
		typedef typename HDS::Vertex   Vertex;
		typedef typename Vertex::Point Point;
		
		// Open obj file for reading.
		std::ifstream _file(mFileName.c_str());
		if(!_file)
		{
			return;
		}
		
		// Count the number of vertices and facets.
		// This is used to reserve memory in HDS.
		std::string _line;
		int _numVertices = 0;
		int _numFacets   = 0;
		while(_file.good())
		{
			std::getline(_file, _line);
			if(_line.size() > 1)
			{
				if(_line[0]=='v' && _line[1]==' ') {++_numVertices;}
				if(_line[0]=='f' && _line[1]==' ') {++_numFacets;}
			}
		}
		
		// Rewind file to beginning for reading data.
		if(!_file.good())
		{
			_file.clear();
		}
		_file.seekg(0);

		// Postcondition: hds is a valid polyhedral surface.
		CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);
		
		// Load the data from OBJ file to HDS.
		B.begin_surface(_numVertices, _numFacets, int((_numVertices + _numFacets - 2)*2.1));
			
			std::string _token;
			while(!_file.eof())
			{
				_token = ""; // Reset token.
				_file >> _token;
				
				// if token is v then its a vertex.
				if(_token=="v")
				{
					double x, y, z;
					_file >> x >> y >> z;
					B.add_vertex(Point(x, y, z));
				}
				
				// There are 4 type of facets.
				// a     only vertex index.
				// a/b   vertex and texture index.
				// a/b/c vertex, texture and normal index.
				// a//c  vertex and normal index.
				else if(_token=="f")
				{
					// Read the remaining line for the facet.
					std::string _line;
					std::getline(_file, _line);
					
					// Split the line into facet's vertices.
					// The length of _vertices is equal to the number of vertices for this face.
					std::istringstream _stream(_line);
					std::vector<std::string> _vertices;
					std::copy(std::istream_iterator<std::string>(_stream), 
							  std::istream_iterator<std::string>(), 
							  std::back_inserter(_vertices));
					
					// For each vertex read only the first number, which is the vertex index.
					B.begin_facet();
					for(size_t i=0 ; i<_vertices.size() ; ++i)
					{
						std::string::size_type _pos = _vertices[i].find('/', 0);
						std::string _indexStr = _vertices[i].substr(0, _pos);
						B.add_vertex_to_facet(stoi(_indexStr)-1); // -1 is because OBJ file uses 1 based index.
					}
					B.end_facet();
				}
			}
			_file.close();
			
		B.end_surface();
	}
	
private:
	
	std::string mFileName;
};


// Import a OBJ file given by fileName to polyhedron.
// TPoly is a type of CGAL::Polyhdeon_3.
template<class TPoly>
void importOBJ(const std::string& fileName, TPoly* polyhedron)
{
	if(polyhedron)
	{
		try
		{
			// Build Polyhedron_3 from the OBJ file.
			BuildCgalPolyhedronFromObj<TPoly::HalfedgeDS> _buildPolyhedron(fileName);
			
			// Calls is_valid at the end. Throws an exception in debug mode if polyhedron is not
			// manifold.
			polyhedron->delegate(_buildPolyhedron);
			
			// CGAL::Assert_exception is thrown in the debug mode when 
			// CGAL::Polyhedron_incremental_builder_3 is destroyed in BuildCgalPolyhedronFromObj.
			// However, in the release mode assertions is disabled and hence no exception is thrown.
			// Thus for uniform error reporting, if the polyhedron is not valid then throw a dummy 
			// exception in release mode.
			if(!polyhedron->is_valid())
			{
				throw CGAL::Assertion_exception("", "", "", 0, "");
			}
		}
		catch(const CGAL::Assertion_exception&)
		{
			std::string _msg = "SMeshLib::importOBJ: Error loading " + fileName;
			throw std::exception(_msg.c_str());
		}
	}
}

};	// End namespace IO.
};	// End namespace SMeshLib.

#endif // _SMESHLIB_IO_IMPORTOBJ_H_
Using importOBJ()
#include "ImportOBJ.h"
#include "CGAL/Simple_cartesian.h"
#include "CGAL/Polyhedron_items_3.h"
#include "CGAL/HalfedgeDS_list.h"
#include "CGAL/Polyhedron_3.h"

typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel, 
	                       CGAL::Polyhedron_items_3, 
						   CGAL::HalfedgeDS_list> CgalPolyhedron;

void testImportOBJ()
{
	CgalPolyhedron _poly;
	SMeshLib::IO::importOBJ("Venus.obj", &_poly);
}
Downloads

ImportOBJ.h
TestImportOBJ.cpp


Zea Stem l.s. – Amscope 50PC Prepared Slides

Zea stem lateral section (l.s.) is the 50th slide in the Amscope 50PC prepared slides. Zea is a genus of true grasses in the family Poaceae of which corn is a member.

Micrographs [07 June 2014]
Amscope 50PC Prepared Slides

This post lists all the micrographs I have done from the Amscope 50PC prepared slides.


Zea Stem c.s. – Amscope 50PC Prepared Slides

Zea stem cross section is the 49th slide in the Amscope 50PC prepared slides. Zea is a genus of true grasses in the family Poaceae of which corn is a member.

Micrographs [07 June 2014]
Amscope 50PC Prepared Slides

This post lists all the micrographs I have done from the Amscope 50PC prepared slides.


Coprinus Mushroom Set – Amscope 50PC Prepared Slides

Cross-section of Coprinus mushroom set is the fourth slide in the Amscope 50PC prepared slides. The Coprinus is a small genus of mushrooms consisting of Coprinus comatus (the shaggy mane) and several of its close relatives [1].

Micrographs

The circular ring in the center is the stem of the mushroom. The center white circle suggests that the stem is hollow from inside. The lines from the stem to the edge forms the cap of the mushroom.

References
  1. http://en.wikipedia.org/wiki/Coprinus
Amscope 50PC Prepared Slides

This post lists all the micrographs I have done from the Amscope 50PC prepared slides.


Cotton Stem – Amscope 50PC Prepared Slides

Cross-section of cotton stem is the fifth slide in the Amscope 50PC prepared slides.

Micrographs [12 April 2014]
Micrographs [28 July 2014]
Amscope 50PC Prepared Slides

This post lists all the micrographs I have done from the Amscope 50PC prepared slides.


Aspergillus – Amscope 50PC Prepared Slides

Aspergillus whole mount (w.m.) is the second slide in the Amscope 50PC prepared slides set. Aspergillus is a genus consisting of several hundred mold species found in various climates worldwide [1]. A mold is a fungus that grows in the form of multicellular filaments called hyphae. In contrast, fungi that can adopt a single celled growth habit are called yeasts [2].

Micrographs
References
  1. http://en.wikipedia.org/wiki/Aspergillus
  2. http://en.wikipedia.org/wiki/Mold
Amscope 50PC Prepared Slides

This post lists all the micrographs I have done from the Amscope 50PC prepared slides.


Paramecium under Microscope in Pond Water

This is a video of a paramecium swimming around in water collected from pond in the Singapore Botanical Gardens.



Cabbage – Amscope 50PC Prepared Slides

The third slide in the Amscope 50PC prepared slides is the longitudinal section of cabbage. The individual cells of the cabbage are clearly visible in the slide.

Micrographs
Amscope 50PC Prepared Slides

This post lists all the micrographs I have done from the Amscope 50PC prepared slides.