Visualization Library

A lightweight C++ OpenGL middleware for 2D/3D graphics
[Home] [Tutorials] [All Classes] [Grouped Classes]

X:/dropbox/visualizationlibrary/src/vlMolecule/Molecule_rendering.cpp

Go to the documentation of this file.
00001 /**************************************************************************************/
00002 /*                                                                                    */
00003 /*  Visualization Library                                                             */
00004 /*  http://www.visualizationlibrary.org                                               */
00005 /*                                                                                    */
00006 /*  Copyright (c) 2005-2010, Michele Bosi                                             */
00007 /*  All rights reserved.                                                              */
00008 /*                                                                                    */
00009 /*  Redistribution and use in source and binary forms, with or without modification,  */
00010 /*  are permitted provided that the following conditions are met:                     */
00011 /*                                                                                    */
00012 /*  - Redistributions of source code must retain the above copyright notice, this     */
00013 /*  list of conditions and the following disclaimer.                                  */
00014 /*                                                                                    */
00015 /*  - Redistributions in binary form must reproduce the above copyright notice, this  */
00016 /*  list of conditions and the following disclaimer in the documentation and/or       */
00017 /*  other materials provided with the distribution.                                   */
00018 /*                                                                                    */
00019 /*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
00020 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
00021 /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
00022 /*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
00023 /*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
00024 /*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
00025 /*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
00026 /*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
00027 /*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
00028 /*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
00029 /*                                                                                    */
00030 /**************************************************************************************/
00031 
00032 #include <vlMolecule/Molecule.hpp>
00033 #include <vlGraphics/GeometryPrimitives.hpp>
00034 #include <vlGraphics/Text.hpp>
00035 #include <vlGraphics/Light.hpp>
00036 
00037 using namespace vl;
00038 
00039 //-----------------------------------------------------------------------------
00040 class EffectCache
00041 {
00042 public:
00043   EffectCache(): mLight(new Light) {}
00044 
00045   void clear() { effects().clear(); }
00046 
00047   Effect* acquireEffect(const fvec4& color)
00048   {
00049     for(unsigned i=0; i<effects().size(); ++i)
00050     {
00051       if (effects()[i]->shader()->gocMaterial()->frontDiffuse() == color)
00052       {
00053         return effects()[i].get();
00054       }
00055     }
00056 
00057     ref<Effect> fx = new Effect;
00058     fx->shader()->enable(EN_DEPTH_TEST);
00059     fx->shader()->enable(EN_CULL_FACE);
00060     fx->shader()->enable(EN_LIGHTING);
00061     fx->shader()->setRenderState(mLight.get(), 0);
00062     fx->shader()->gocMaterial()->setDiffuse(color);
00063     effects().push_back(fx.get());
00064     return fx.get();
00065   }
00066 
00067   const std::vector< ref<Effect> >& effects() const { return mEffects; }
00068   std::vector< ref<Effect> >& effects() { return mEffects; }
00069 
00070   const Light* light() const { return mLight.get(); }
00071   Light* light() { return mLight.get(); }
00072 
00073 protected:
00074   std::vector< ref<Effect> > mEffects;
00075   ref<Light> mLight;
00076 };
00077 //-----------------------------------------------------------------------------
00078 class AtomGeometryCache
00079 {
00080 public:
00081   AtomGeometryCache(): mDetail(1) {}
00082 
00083   void clear() { mGeometryMap.clear(); }
00084   const std::map< float, ref<Geometry> >& geometryMap() const { return mGeometryMap; }
00085   std::map< float, ref<Geometry> >& geometryMap() { return mGeometryMap; }
00086   Geometry* acquireAtomGeometry(float radius)
00087   {
00088     std::map< float, ref<Geometry> >::iterator it = geometryMap().find(radius);
00089     if (it!=geometryMap().end())
00090       return it->second.get();
00091     else
00092     {
00093       ref<Geometry> sphere = makeIcosphere( vec3(0,0,0), radius*2.0f, detail() );
00094       geometryMap()[radius] = sphere;
00095       return sphere.get();
00096     }
00097   }
00098 
00099   int detail() const { return mDetail; }
00100   void setDetail(int detail) { mDetail = detail; }
00101 
00102 protected:
00103   std::map< float, ref<Geometry> > mGeometryMap;
00104   int mDetail;
00105 };
00106 //-----------------------------------------------------------------------------
00107 class BondGeometryCache
00108 {
00109   class BondKey
00110   {
00111   public:
00112     float height;
00113     fvec4 col1;
00114     fvec4 col2;
00115     ECapsuleCap top_cap;
00116     ECapsuleCap bottom_cap;
00117 
00118     BondKey(float h, const fvec4& c1, const fvec4& c2, ECapsuleCap topcap, ECapsuleCap bottomcap): height(h), col1(c1), col2(c2), top_cap(topcap), bottom_cap(bottomcap) {}
00119     bool operator==(const BondKey& other) const
00120     {
00121       return height     == other.height  &&
00122              col1       == other.col1    &&
00123              col2       == other.col2    &&
00124              top_cap    == other.top_cap &&
00125              bottom_cap == other.bottom_cap;
00126     }
00127     bool operator<(const BondKey& other) const
00128     {
00129       if (top_cap!=other.top_cap)
00130         return top_cap<other.top_cap;
00131       else
00132       if (bottom_cap!=other.bottom_cap)
00133         return bottom_cap<other.bottom_cap;
00134       else
00135       if (height!=other.height)
00136         return height<other.height;
00137       else
00138       if (col1!=other.col1)
00139         return col1<other.col1;
00140       else
00141         return col2<other.col2;
00142     }
00143   };
00144 public:
00145   BondGeometryCache(): mDetail(20), mDiameter(0.20f), mQuantization(100.0f) {}
00146 
00147   void clear() { mGeometryMap.clear(); }
00148   const std::map< BondKey, ref<Geometry> >& geometryMap() const { return mGeometryMap; }
00149   std::map< BondKey, ref<Geometry> >& geometryMap() { return mGeometryMap; }
00150   Geometry* acquireBondGeometry(float length, const fvec4& c1, const fvec4& c2, ECapsuleCap top_cap, ECapsuleCap bottom_cap)
00151   {
00152     float quant_lenght = int(length*quantization()) / quantization();
00153     BondKey key(quant_lenght,c1,c2,top_cap,bottom_cap);
00154     std::map< BondKey, ref<Geometry> >::iterator it = geometryMap().find( key );
00155     if (it!=geometryMap().end())
00156     {
00157       VL_CHECK(it->first == key)
00158       return it->second.get();
00159     }
00160     else
00161     {
00162       ref<Geometry> cylinder = makeCapsule( diameter()/2.0f, quant_lenght+2.0f/quantization(), detail(), top_cap, bottom_cap, c2, c1 );
00163       cylinder->computeNormals();
00164       geometryMap()[key] = cylinder;
00165       return cylinder.get();
00166     }
00167   }
00168 
00169   int detail() const { return mDetail; }
00170   void setDetail(int detail) { mDetail = detail; }
00171 
00172   float diameter() const { return mDiameter; }
00173   void setDiameter(float diameter) { mDiameter = diameter; }
00174 
00175   float quantization() const { return mQuantization; }
00176   void setQuantization(float quantization) { mQuantization = quantization; }
00177 
00178 protected:
00179   std::map< BondKey, ref<Geometry> > mGeometryMap;
00180   int mDetail;
00181   float mDiameter;
00182   float mQuantization;
00183 };
00184 //-----------------------------------------------------------------------------
00185 void Molecule::prepareForRendering()
00186 {
00187   actorTree()->actors()->clear();
00188   transformTree()->eraseAllChildren();
00189 
00190   switch(moleculeStyle())
00191   {
00192     case MS_Wireframe:    wireframeStyle();    generateRings(); break;
00193     case MS_BallAndStick: ballAndStickStyle(); generateRings(); break;
00194     case MS_Sticks:       sticksStyle();       generateRings(); break;
00195     case MS_AtomsOnly:    atomsStyle();                         break;
00196   }
00197   generateAtomLabels();
00198   transformTree()->computeWorldMatrixRecursive();
00199 }
00200 //-----------------------------------------------------------------------------
00201 void Molecule::generateAtomLabel(const Atom* atom, Transform* tr)
00202 {
00203   if (atomLabelTemplate()->font() && 
00204       showAtomNames()             &&
00205       atom->visible()             &&
00206       atom->showAtomName()        )
00207   {
00208     ref<Text> text = new Text;
00209     // text label
00210     text->setText( atom->atomName().c_str() );
00211     // text template style
00212     text->setViewportAlignment( atomLabelTemplate()->viewportAlignment() );
00213     text->setTextAlignment( atomLabelTemplate()->textAlignment() );
00214     text->setShadowVector( atomLabelTemplate()->shadowVector() );
00215     text->setShadowEnabled( atomLabelTemplate()->shadowEnabled() );
00216     text->setShadowColor( atomLabelTemplate()->shadowColor() );
00217     text->setOutlineEnabled( atomLabelTemplate()->outlineEnabled() );
00218     text->setOutlineColor( atomLabelTemplate()->outlineColor() );
00219     text->setMode( atomLabelTemplate()->mode() );
00220     text->setMargin( atomLabelTemplate()->margin() );
00221     text->setKerningEnabled( atomLabelTemplate()->kerningEnabled() );
00222     text->setFont( atomLabelTemplate()->font() );
00223     text->setColor( atomLabelTemplate()->color() );
00224     text->setBorderEnabled( atomLabelTemplate()->borderEnabled() );
00225     text->setBorderColor( atomLabelTemplate()->borderColor() );
00226     text->setBackgroundEnabled( atomLabelTemplate()->backgroundEnabled() );
00227     text->setBackgroundColor( atomLabelTemplate()->backgroundColor() );
00228     text->setAlignment( atomLabelTemplate()->alignment() );
00229     // text actor
00230     ref<Actor> text_act = new Actor( text.get(), mAtomLabelEffect.get(), tr );
00231     actorTree()->actors()->push_back(text_act.get());
00232   }
00233 }
00234 //-----------------------------------------------------------------------------
00235 void Molecule::generateAtomLabels()
00236 {
00237   for(unsigned i=0; i<atoms().size(); ++i)
00238   {
00239     ref<Transform> tr = new Transform(mat4::getTranslation((vec3)atoms()[i]->coordinates()));
00240     transformTree()->addChild(tr.get());
00241     generateAtomLabel(atoms()[i].get(), tr.get());
00242   }
00243 }
00244 //-----------------------------------------------------------------------------
00245 void Molecule::wireframeStyle()
00246 {
00247   // no maps are generated for this style.
00248   mAtomToActorMap.clear();
00249   mActorToAtomMap.clear();
00250   mBondToActorMap.clear();
00251   mActorToBondMap.clear();
00252 
00253   ref<Geometry> geom = new Geometry;
00254   ref<ArrayFloat3> points = new ArrayFloat3;
00255   geom->setVertexArray(points.get());
00256   ref<ArrayFloat4> colors = new ArrayFloat4;
00257   geom->setColorArray(colors.get());
00258   std::vector<fvec3> pt;
00259   std::vector<fvec4> cols;
00260   for(unsigned ibond=0; ibond<bonds().size(); ++ibond)
00261   {
00262     Bond* b = bond(ibond);
00263     if (b->visible() && b->atom1()->visible() && b->atom2()->visible())
00264     {
00265       fvec4 c1 = b->color();
00266       fvec4 c2 = b->color();
00267       if (b->useAtomColors())
00268       {
00269         c1 = b->atom1()->color();
00270         c2 = b->atom2()->color();
00271       }
00272       if (c1 == c2)
00273       {
00274         pt.push_back( b->atom1()->coordinates() );
00275         pt.push_back( b->atom2()->coordinates() );
00276         cols.push_back(c1);
00277         cols.push_back(c1);
00278       }
00279       else
00280       {
00281         fvec3 center = (b->atom1()->coordinates() + b->atom2()->coordinates())/2.0f;
00282         pt.push_back( b->atom1()->coordinates() );
00283         pt.push_back( center );
00284         pt.push_back( center );
00285         pt.push_back( b->atom2()->coordinates() );
00286         cols.push_back(c1);
00287         cols.push_back(c1);
00288         cols.push_back(c2);
00289         cols.push_back(c2);
00290       }
00291     }
00292   }
00293   points->initFrom(pt);
00294   colors->initFrom(cols);
00295   geom->drawCalls()->push_back(new DrawArrays(PT_LINES, 0, (int)points->size()));
00296 
00297   ref<Effect> fx = new Effect;
00298   fx->shader()->enable(EN_DEPTH_TEST);
00299   if (smoothLines())
00300   {
00301     fx->shader()->enable(EN_BLEND);
00302     fx->shader()->enable(EN_LINE_SMOOTH);
00303   }
00304   if (lineWidth() != 1.0f)
00305     fx->shader()->gocLineWidth()->set(lineWidth());
00306 
00307   actorTree()->actors()->push_back( new Actor(geom.get(), fx.get(), NULL) );
00308 }
00309 //-----------------------------------------------------------------------------
00310 void Molecule::atomsStyle()
00311 {
00312   mAtomToActorMap.clear();
00313   mActorToAtomMap.clear();
00314   mBondToActorMap.clear();
00315   mActorToBondMap.clear();
00316 
00317   EffectCache fx_cache;
00318   AtomGeometryCache atom_geom_cache;
00319   atom_geom_cache.setDetail(atomDetail());
00320   for(unsigned iatom=0; iatom<atoms().size(); ++iatom)
00321   {
00322     if (atom(iatom)->visible())
00323     {
00324       Effect* fx = fx_cache.acquireEffect(atom(iatom)->color());
00325       float r = atom(iatom)->radius();
00326       ref<Geometry> ball = atom_geom_cache.acquireAtomGeometry(r);
00327       ref<Actor> atom_act = new Actor( ball.get(), fx, new Transform );
00328       atom_act->transform()->setLocalMatrix( mat4::getTranslation( (vec3)atom(iatom)->coordinates()) );
00329       transformTree()->addChild(atom_act->transform());
00330       actorTree()->actors()->push_back( atom_act.get() );
00331 
00332       // actor -> atom map
00333       if (isActorToMoleculeMapEnabled())
00334         mActorToAtomMap.insert( std::pair< ref<Actor>, ref<Atom> >(atom_act, atom(iatom)) );
00335       // atom -> actor map
00336       if (isMoleculeToActorMapEnabled())
00337         mAtomToActorMap.insert( std::pair< ref<Atom>, ref<Actor> >(atom(iatom), atom_act) );
00338     }
00339   }
00340 }
00341 //-----------------------------------------------------------------------------
00342 void Molecule::ballAndStickStyle()
00343 {
00344   mAtomToActorMap.clear();
00345   mActorToAtomMap.clear();
00346   mBondToActorMap.clear();
00347   mActorToBondMap.clear();
00348 
00349   EffectCache fx_cache;
00350   AtomGeometryCache atom_geom_cache;
00351   atom_geom_cache.setDetail(atomDetail());
00352   for(unsigned int iatom=0; iatom<atoms().size(); ++iatom)
00353   {
00354     if (atom(iatom)->visible())
00355     {
00356       Effect* fx = fx_cache.acquireEffect(atom(iatom)->color());
00357       float r = atom(iatom)->radius();
00358       ref<Geometry> ball = atom_geom_cache.acquireAtomGeometry(r);
00359       
00360       // mic fixme:
00361       // it would be nice to have a pool to accelerate Actor and Transform allocation
00362 
00363       ref<Actor> atom_act = new Actor( ball.get(), fx, new Transform );
00364       atom_act->transform()->setLocalMatrix( mat4::getTranslation( (vec3)atom(iatom)->coordinates()) );
00365       transformTree()->addChild(atom_act->transform());
00366       actorTree()->actors()->push_back( atom_act.get() );
00367 
00368       // actor -> atom map
00369       if (isActorToMoleculeMapEnabled())
00370         mActorToAtomMap.insert( std::pair< ref<Actor>, ref<Atom> >(atom_act, atom(iatom)) );
00371       // atom -> actor map
00372       if (isMoleculeToActorMapEnabled())
00373         mAtomToActorMap.insert( std::pair< ref<Atom>, ref<Actor> >(atom(iatom), atom_act) );
00374     }
00375   }
00376 
00377   ref<Effect> fx = new Effect;
00378   fx->shader()->enable(EN_DEPTH_TEST);
00379   fx->shader()->enable(EN_CULL_FACE);
00380   fx->shader()->gocMaterial()->setColorMaterialEnabled(true);
00381   fx->shader()->gocLightModel()->setTwoSide(false);
00382   fx->shader()->enable(EN_LIGHTING);
00383   fx->shader()->setRenderState( fx_cache.light(), 0 );
00384   // fx->shader()->gocPolygonMode()->set(PM_LINE, PM_LINE);
00385 
00386   BondGeometryCache bond_geom_cache;
00387   bond_geom_cache.setDetail(bondDetail());
00388   for(unsigned int ibond=0; ibond<bonds().size(); ++ibond)
00389   {
00390     if (bond(ibond)->visible() && bond(ibond)->atom1()->visible() && bond(ibond)->atom2()->visible())
00391     {
00392       Bond* b = bond(ibond);
00393       fvec4 c1 = b->color();
00394       fvec4 c2 = b->color();
00395       if (b->useAtomColors())
00396       {
00397         c1 = b->atom1()->color();
00398         c2 = b->atom2()->color();
00399       }
00400       float len = (b->atom1()->coordinates() - b->atom2()->coordinates()).length();
00401       float diam = b->radius()*2.0f;
00402       bond_geom_cache.setDiameter(diam);
00403       ref<Geometry> geom = bond_geom_cache.acquireBondGeometry(len,c1,c2,CC_NoCap,CC_NoCap);
00404       ref<Actor> bond_act = new Actor( geom.get(), fx.get(), new Transform );
00405       transformTree()->addChild(bond_act->transform());
00406       fvec3 center = (b->atom1()->coordinates() + b->atom2()->coordinates()) / 2.0f;
00407       fvec3 direction = (b->atom2()->coordinates() - b->atom1()->coordinates()).normalize();
00408       fmat4 mat = fmat4::getTranslation(center) * fmat4::getRotation(fvec3(0,1,0), direction);
00409       bond_act->transform()->setLocalMatrix( (mat4)mat );
00410       actorTree()->actors()->push_back( bond_act.get() );
00411 
00412       // actor -> bond map
00413       if (isActorToMoleculeMapEnabled())
00414         mActorToBondMap.insert( std::pair< ref<Actor>, ref<Bond> >(bond_act, bond(ibond)) );
00415       // bond -> actor map
00416       if (isMoleculeToActorMapEnabled())
00417         mBondToActorMap.insert( std::pair< ref<Bond>, ref<Actor> >(bond(ibond), bond_act) );
00418     }
00419   }
00420 }
00421 //-----------------------------------------------------------------------------
00422 void Molecule::sticksStyle()
00423 {
00424   mAtomToActorMap.clear();
00425   mActorToAtomMap.clear();
00426   mBondToActorMap.clear();
00427   mActorToBondMap.clear();
00428 
00429   ref<Effect> fx = new Effect;
00430   fx->shader()->enable(EN_DEPTH_TEST);
00431   fx->shader()->enable(EN_CULL_FACE);
00432   fx->shader()->gocMaterial()->setColorMaterialEnabled(true);
00433   fx->shader()->gocLightModel()->setTwoSide(false);
00434   fx->shader()->enable(EN_LIGHTING);
00435   fx->shader()->setRenderState( new Light, 0 );
00436   /*fx->shader()->gocPolygonMode()->set(PM_LINE, PM_LINE);*/
00437 
00438   BondGeometryCache bond_geom_cache;
00439   bond_geom_cache.setDetail(bondDetail());
00440   for(unsigned int ibond=0; ibond<bonds().size(); ++ibond)
00441   {
00442     if (bond(ibond)->visible() && bond(ibond)->atom1()->visible() && bond(ibond)->atom2()->visible())
00443     {
00444       Bond* b = bond(ibond);
00445       fvec4 c1 = b->color();
00446       fvec4 c2 = b->color();
00447       if (b->useAtomColors())
00448       {
00449         c1 = b->atom1()->color();
00450         c2 = b->atom2()->color();
00451       }
00452       float len = (b->atom1()->coordinates() - b->atom2()->coordinates()).length();
00453       float diam = b->radius()*2.0f;
00454       bond_geom_cache.setDiameter(diam);
00455       ref<Geometry> geom = bond_geom_cache.acquireBondGeometry(len,c1,c2,CC_RoundedCap,CC_RoundedCap);
00456       ref<Actor> bond_act = new Actor( geom.get(), fx.get(), new Transform );
00457       transformTree()->addChild(bond_act->transform());
00458       fvec3 center = (b->atom1()->coordinates() + b->atom2()->coordinates()) / 2.0f;
00459       fvec3 direction = (b->atom2()->coordinates() - b->atom1()->coordinates()).normalize();
00460       fmat4 mat = fmat4::getTranslation(center) * fmat4::getRotation(fvec3(0,1,0), direction);
00461       bond_act->transform()->setLocalMatrix( (mat4)mat );
00462       actorTree()->actors()->push_back( bond_act.get() );
00463 
00464       // actor -> bond map
00465       if (isActorToMoleculeMapEnabled())
00466         mActorToBondMap.insert( std::pair< ref<Actor>, ref<Bond> >(bond_act, bond(ibond)) );
00467       // bond -> actor map
00468       if (isMoleculeToActorMapEnabled())
00469         mBondToActorMap.insert( std::pair< ref<Bond>, ref<Actor> >(bond(ibond), bond_act) );
00470     }
00471   }
00472 }
00473 //-----------------------------------------------------------------------------
00474 void Molecule::generateRings()
00475 {
00476   if (!cycles().empty())
00477   {
00478     ref<Geometry> geom = new Geometry;
00479     ref<ArrayFloat3> points = new ArrayFloat3;
00480     geom->setVertexArray(points.get());
00481     ref<ArrayFloat4> colors = new ArrayFloat4;
00482     geom->setColorArray(colors.get());
00483     std::vector<fvec3> pt;
00484     std::vector<fvec4> cols;
00485     for(unsigned icycle=0; icycle<cycles().size(); ++icycle)
00486     {
00487       AABB aabb;
00488       for(unsigned iatom=0; iatom<cycle(icycle).size(); ++iatom)
00489         aabb += (vec3)cycle(icycle)[iatom]->coordinates();
00490       fvec3 center = (fvec3)aabb.center();
00491 
00492       for(unsigned iatom=0; iatom<cycle(icycle).size(); ++iatom)
00493       {
00494         int iatom2 = (iatom+1) % cycle(icycle).size();
00495         fvec3 v1 = cycle(icycle)[iatom ]->coordinates();
00496         fvec3 v2 = cycle(icycle)[iatom2]->coordinates();
00497         v1 += (center-v1).normalize() * ringOffset();
00498         v2 += (center-v2).normalize() * ringOffset();
00499         pt.push_back( v1 );
00500         pt.push_back( v2 );
00501         cols.push_back( aromaticRingColor() );
00502         cols.push_back( aromaticRingColor() );
00503       }
00504     }
00505     points->initFrom(pt);
00506     colors->initFrom(cols);
00507     geom->drawCalls()->push_back(new DrawArrays(PT_LINES, 0, (int)points->size()));
00508 
00509     ref<Effect> fx = new Effect;
00510     fx->shader()->enable(EN_DEPTH_TEST);
00511 
00512     actorTree()->actors()->push_back( new Actor(geom.get(), fx.get(), NULL) );
00513   }
00514 }
00515 //-----------------------------------------------------------------------------

Visualization Library 2011.09.1160 Reference Documentation
Copyright 2005-2011 Michele Bosi. All rights reserved.
Updated on Thu May 2 2013 13:40:43.
Permission is granted to use this page to write and publish articles regarding Visualization Library.