ROOT logo
// $Id: TriMesh.cxx 2548 2011-10-10 07:03:57Z matevz $

// Copyright (C) 1999-2008, Matevz Tadel. All rights reserved.
// This file is part of GLED, released under GNU General Public License version 2.
// For the licensing terms see $GLEDSYS/LICENSE or http://www.gnu.org/.

#include "TriMesh.h"
#include <Glasses/ParaSurf.h>
#include <Glasses/RectTerrain.h>
#include <Glasses/GTSurf.h>
#include <Glasses/ZImage.h>
#include <Glasses/RGBAPalette.h>
#include "TriMesh.c7"

#include <GTS/GTS.h>

#include <Opcode/Opcode.h>

#include <TPRegexp.h>
#include <TMath.h>
#include <TF3.h>

#include <fstream>


TriMeshColorArraySource*
TriMeshColorArraySource::CastLens(const Exc_t& eh, ZGlass* lens, Bool_t null_ok)
{
  if (lens == 0)
  {
    if (null_ok)
      return 0;
    else
      throw eh + "null lens that should be trimesh-color-array-source.";
  }
  TriMeshColorArraySource *cas = dynamic_cast<TriMeshColorArraySource*>(lens);
  if (cas)
    return cas;
  else
    throw eh + "lens is supposed to be a TriMeshColorArraySource.";
}


//__________________________________________________________________________
//
// Wrapper over TringTvor (triangulation data) and Opcode structures.
//
// User is responsible for (re)creation of aabboxes in TringTvor.
//
// Structure VConnData must be reduced (possibly use quantization to shorts.
// Structure VertexData should be extended with per-point values.
// I guess something like gl uniform varibles (including vector forms).

ClassImp(TriMesh);

/**************************************************************************/

void TriMesh::_init()
{
  mTTvor     = 0;

  mVolume    = 0;  // Assumed volume, set by user.
  mXYArea    = 0;  // Assumed surface covered on ground, set by user.

  mM         = 0;  // Assumed mass of the mesh or object it represents.
  mSurface   = 0;  // Sum of trianlge surfaces.
  mSection.Zero(); // Sections / side-view areas of the mesh. This is sum of
                   // front-facing triangle projections, so can be wrong.
  mCOM.Zero();     // Center-of-mass
  mJ.Zero();       // Angular-inertia along major axes / insufficient.

  mOPCModel  = 0;
  mOPCMeshIf = 0;
}

TriMesh::~TriMesh()
{
  delete mTTvor;
  delete mOPCModel;
  delete mOPCMeshIf;
}

void TriMesh::assert_tvor(const Exc_t& eh)
{
  if (!mTTvor)
    throw eh + "mTTvor is null.";
}

//==============================================================================

void TriMesh::AssertVertexColorArray()
{
  mTTvor->AssertCols();
}

UChar_t* TriMesh::GetVertexColorArray()
{
  return mTTvor->HasCols() ? mTTvor->Cols() : 0;
}

UChar_t* TriMesh::GetTriangleColorArray()
{
  return mTTvor->HasTringCols() ? mTTvor->TringCols() : 0;
}

void TriMesh::ColorArraysModified()
{
  StampReqTring(FID());
}

//==============================================================================

void TriMesh::ResetTTvorDependants()
{
  delete mOPCModel;  mOPCModel  = 0;
  delete mOPCMeshIf; mOPCMeshIf = 0;

  mVDataVec.resize(0);
}

//==============================================================================

namespace
{
inline void add_Js(HPointF& J, HPointF& ND, const Opcode::Point& r, Float_t m)
{
  const Float_t xs = r.x*r.x, ys = r.y*r.y, zs = r.z*r.z;
  J.x += m * (ys + zs);  ND.x -= m * r.y*r.z;
  J.y += m * (zs + xs);  ND.y -= m * r.x*r.z;
  J.z += m * (xs + ys);  ND.z -= m * r.x*r.y;
}
}

void TriMesh::SetMassAndSpeculate(Float_t mass, Float_t mass_frac_on_mesh)
{
  // Set mass of the mesh and calculate center of mass and Jz (should
  // extend for full matrix but let's keep it simple for now) with a
  // simple algorithm: distribute mass_frac_on_mesh of mass over the
  // triangles of the mesh.
  //
  // As J_spehere = 2/5 m*r^2, the default value for mass_frac_on_mesh = 0.4.

  // !!! Doubles should be used for all accumulators when adding up
  // quantites over vertices/triangles.

  mM       = mass;
  mSurface = 0;
  mSection.Zero();
  mCOM.Zero();
  mJ.Zero();

  const Int_t nTrings = mTTvor->mNTrings;

  std::vector<Float_t> tri_surfs(nTrings);

  Int_t *vts;
  { // Sum surface
    Opcode::Point *p0, e1, e2, cp;
    for (Int_t tring = 0; tring < nTrings; ++tring)
    {
      vts = mTTvor->Triangle(tring);

      using namespace Opcode;
      p0 = (Point*) mTTvor->Vertex(vts[0]);
      e1.Set(mTTvor->Vertex(vts[1])); e1.Sub(*p0);
      e2.Set(mTTvor->Vertex(vts[2])); e2.Sub(*p0);
      cp.Cross(e1, e2);

      tri_surfs[tring] = 0.5f * cp.Magnitude();
      mSurface += tri_surfs[tring];

      if (cp.x > 0) mSection.x += 0.5f * cp.x;
      if (cp.y > 0) mSection.y += 0.5f * cp.y;
      if (cp.z > 0) mSection.z += 0.5f * cp.z;
    }
  }
  Opcode::Point com(0, 0, 0);
  {
    Float_t oneoso3 = Opcode::INV3 / mSurface; // one over surface over 3
    Float_t surfptv;                   // surface per triangle vertex
    for (Int_t tring = 0; tring < nTrings; ++tring)
    {
      vts     = mTTvor->Triangle(tring);
      surfptv = oneoso3 * tri_surfs[tring];

      using namespace Opcode;
      com.TMac(* ((Point*) mTTvor->Vertex(vts[0])), surfptv);
      com.TMac(* ((Point*) mTTvor->Vertex(vts[1])), surfptv);
      com.TMac(* ((Point*) mTTvor->Vertex(vts[2])), surfptv);
    }
    mCOM.Set(com);
  }
  {
    // Calculate components of inertia tensor.
    // Should:
    // 1. diagonalize
    // 2. setup mesh-to-extendio transform
    //    extendio specific, once special loads are allowed

    HPointF I, J; // diagonal, off-diagonal elements

    Float_t mpso3 = Opcode::INV3 * mass_frac_on_mesh * mass / mSurface; // mass per surface over 3
    Float_t mptv; // mass per triangle vertex
    Opcode::Point r;
    for (Int_t tring = 0; tring < nTrings; ++tring)
    {
      vts  = mTTvor->Triangle(tring);
      mptv = mpso3 * tri_surfs[tring];

      r.Set(mTTvor->Vertex(vts[0])); r.Sub(com); add_Js(I, J, r, mptv);
      r.Set(mTTvor->Vertex(vts[1])); r.Sub(com); add_Js(I, J, r, mptv);
      r.Set(mTTvor->Vertex(vts[2])); r.Sub(com); add_Js(I, J, r, mptv);
    }

    mJ = I;

    /*
    // Diagonalization of inertia tensor, just trying it as it is not used.
    // #include <TVectorT.h>
    // #include <TMatrixTSym.h>
    printf("Diagonalizing inertia tensor of TriMesh %s:\n", GetName());
    printf("    Ixx=%.3f Iyy=%.3f Izz=%.3f\n", I.x, I.y, I.z);
    printf("    Iyz=%.3f Ixz=%.3f Ixy=%.3f\n", J.x, J.y, J.z);

    TMatrixTSym<float> m(3);
    m(0,0) = I.x; m(0,1) = m(1,0) = J.z; m(0,2) = m(2,0) = J.y;
    m(1,1) = I.y; m(1,2) = m(2,1) = J.x;
    m(2,2) = I.z;
    m.Print();
    TVectorT<float> eval;
    TMatrixT<float> evec = m.EigenVectors(eval);
    printf("Eigen vals: %f, %f, %f\n", eval(0), eval(1), eval(2));
    evec.Print();
    */
  }

  Stamp(FID());
}

void TriMesh::SetMassFromBBox(Float_t sfac, Float_t hfac, Float_t density,
                              Float_t mass_frac_on_mesh)
{
  // Estimate XY-area, volume and mass from given parameters and
  // bounding-box of tring-tvor.

  mXYArea = sfac * mTTvor->BoundingBoxXYArea();
  mVolume = hfac * 2.0f * mTTvor->mCtrExtBox[5] * mXYArea;
  SetMassAndSpeculate(density * mVolume, mass_frac_on_mesh);
}

//==============================================================================

void TriMesh::StdSurfacePostImport()
{
  CalculateBoundingBox();
  BuildOpcStructs();
  BuildVertexConnections();
  mParaSurf->FindMinMaxFGH(this);
}

void TriMesh::StdDynamicoPostImport()
{
  CalculateBoundingBox();
  BuildOpcStructs();
}

/**************************************************************************/

void TriMesh::BuildOpcStructs()
{
  static const Exc_t _eh("TriMesh::BuildOpcStructs ");

  assert_tvor(_eh);

  using namespace Opcode;

  delete mOPCModel;
  delete mOPCMeshIf;
  mOPCModel  = new Model;
  mOPCMeshIf = new MeshInterface;
  mOPCMeshIf->SetNbTriangles(mTTvor->mNTrings);
  mOPCMeshIf->SetNbVertices(mTTvor->mNVerts);
  mOPCMeshIf->SetPointers((IndexedTriangle*) mTTvor->Trings(),
                          (Point*)           mTTvor->Verts());

  OPCODECREATE OPCC;
  OPCC.mIMesh = mOPCMeshIf;

  bool status = mOPCModel->Build(OPCC);
  if ( ! status)
    throw _eh + "Build failed for " + Identify() + ".";
}

void TriMesh::AssertOpcStructs()
{
  if (mOPCModel == 0 || mOPCMeshIf == 0)
    BuildOpcStructs();
}

/**************************************************************************/
// TringTvor interface
/**************************************************************************/

void TriMesh::CalculateBoundingBox()
{
  // Calculates bounding box of the mesh.

  static const Exc_t _eh("TriMesh::CalculateBoundingBox ");

  assert_tvor(_eh);

  mTTvor->CalculateBoundingBox();

  // Float_t *b = mTTvor->mMinMaxBox, *e = mTTvor->mCtrExtBox;
  // printf("%s %s\n"
  //        "  min = % 8.4f, % 8.4f, % 8.4f  max = % 8.4f, % 8.4f, % 8.4f\n"
  //        "  ctr = % 8.4f, % 8.4f, % 8.4f  ext = % 8.4f, % 8.4f, % 8.4f\n",
  //        _eh.Data(), Identify().Data(),
  //        b[0], b[1], b[2], b[3], b[4], b[5],
  //        e[0], e[1], e[2], e[3], e[4], e[5]);

  // mStampReqTring = Stamp(FID());
}

void TriMesh::GenerateVertexNormals()
{
  static const Exc_t _eh("TriMesh::GenerateVertexNormals ");

  assert_tvor(_eh);

  mTTvor->GenerateVertexNormals();
  mStampReqTring = Stamp(FID());
}

void TriMesh::GenerateTriangleNormals()
{
  static const Exc_t _eh("TriMesh::GenerateTriangleNormals ");

  assert_tvor(_eh);

  mTTvor->GenerateTriangleNormals();
  mStampReqTring = Stamp(FID());
}

/**************************************************************************/
// RectTerrain import
/**************************************************************************/

void TriMesh::ImportRectTerrain(RectTerrain* rt, Bool_t colp, Bool_t texp)
{
  static const Exc_t _eh("TriMesh::ImportRectTerrain ");

  if(rt == 0)
    throw(_eh + "null argument passed.");

  TringTvor* tt = 0;
  { GLensReadHolder _rlck(rt);
    // Here we request vertex (smooth) and triangle (flat) sections.
    tt = rt->SpawnTringTvor(true, true, colp, texp);
    if(tt == 0)
      throw(_eh + "tvor null after MakeTringTvor().");
  }
  GLensWriteHolder _wlck(this);
  delete mTTvor;
  mTTvor = tt;
  mStampReqTring = Stamp(FID());
}

/**************************************************************************/
// GTSurf import/export
/**************************************************************************/

void TriMesh::ImportGTSurf(GTSurf* gts)
{
  static const Exc_t _eh("TriMesh::ImportGTSurf ");

  using namespace GTS;

  if (!gts)
    throw _eh + "called with null argument.";
  if (!gts->GetSurf())
    throw _eh + "GtsSurface is null.";

  struct Dumper
  {
    map<GtsVertex*, int>  m_map;
    TringTvor            *m_tvor;
    int                   m_vcount;
    int                   m_tcount;

    Dumper(TringTvor* t) : m_tvor(t), m_vcount(0), m_tcount(0) {}

    static void vdump(GtsVertex* v, Dumper* arg)
    {
      arg->m_map[v] = arg->m_vcount;
      Float_t *q = arg->m_tvor->Vertex(arg->m_vcount);
      q[0] = v->p.x; q[1] = v->p.y; q[2] = v->p.z;
      ++arg->m_vcount;
    }

    static void fdump(GtsFace* f, Dumper* arg)
    {
      GtsVertex *a, *b, *c;
      gts_triangle_vertices(&f->triangle, &a, &b, &c);
      Int_t *q = arg->m_tvor->Triangle(arg->m_tcount);
      q[0] = arg->m_map[a]; q[1] = arg->m_map[b]; q[2] = arg->m_map[c];
      ++arg->m_tcount;
    }

  };

  GtsSurface* surf = gts->GetSurf();
  TringTvor* tt = new TringTvor(gts_surface_vertex_number(surf),
                                gts_surface_face_number  (surf));

  Dumper arg(tt);
  gts_surface_foreach_vertex(surf, (GtsFunc) Dumper::vdump, &arg);
  gts_surface_foreach_face  (surf, (GtsFunc) Dumper::fdump, &arg);

  GLensWriteHolder _wlck(this);
  delete mTTvor;
  mTTvor = tt;
  mStampReqTring = Stamp(FID());
}

void TriMesh::ExportGTSurf(GTSurf* gts)
{
  static const Exc_t _eh("TriMesh::ExportGTSurf ");

  using namespace GTS;

  // Prepare edge data

  hEdge_t edge_map;
  Int_t   edge_cnt = fill_edge_map(edge_map, 0);
  printf("%sCounting edges => %d, map-size = %d.\n", _eh.Data(),
         edge_cnt, (int)edge_map.size());

  // Construct surface

  GtsSurface* surf = MakeDefaultSurface();

  GtsVertex **vertices = new GtsVertex*[mTTvor->mNVerts];
  {
    Float_t* va = mTTvor->Verts();
    for(Int_t v=0; v<mTTvor->mNVerts; ++v) {
      vertices[v] = gts_vertex_new(surf->vertex_class,
                                   va[0], va[1], va[2]);
      va += 3;
    }
  }

  GtsEdge **edges = new GtsEdge*[edge_cnt];
  {
    for(hEdge_i e=edge_map.begin(); e!=edge_map.end(); ++e) {
      edges[e->second] = gts_edge_new(surf->edge_class,
                                      vertices[e->first.v1],
                                      vertices[e->first.v2]);
    }
  }

  {
    Int_t* ta = mTTvor->Trings();
    for(Int_t t=0; t<mTTvor->mNTrings; ++t) {
      Int_t e1, e2, e3;
      e1 = edge_map.find(Edge(ta[0], ta[1]))->second;
      e2 = edge_map.find(Edge(ta[1], ta[2]))->second;
      e3 = edge_map.find(Edge(ta[2], ta[0]))->second;
      GtsFace * new_face = gts_face_new(surf->face_class,
                                        edges[e1], edges[e2], edges[e3]);
      gts_surface_add_face (surf, new_face);
      ta += 3;
    }
  }

  gts->ReplaceSurface(surf);
}


/**************************************************************************/
// Oolite DAT format import
/**************************************************************************/

namespace
{
  TString next_oolite_dat_line(ifstream& f, TPMERegexp& comment_re)
  {
    while (!f.eof())
    {
      TString l;
      l.ReadLine(f, kTRUE);
      if (comment_re.Match(l) == 0)
	return l;
    }
    return TString();
  }
}

void TriMesh::ImportOoliteDAT(const TString& filename, Bool_t invert_triangles)
{
  static const Exc_t _eh("TriMesh::ImportOoliteDAT ");

  ifstream f(filename);
  TPMERegexp comment_re("^\\s*//", "o");

  TString l;

  Int_t nv, nt;
  l = next_oolite_dat_line(f, comment_re);
  if (sscanf(l, "NVERTS %d", &nv) != 1) throw _eh + "Expect NVERTS";
  l = next_oolite_dat_line(f, comment_re);
  if (sscanf(l, "NFACES %d", &nt) != 1) throw _eh + "Expect NFACES";

  delete mTTvor;
  mTTvor = new TringTvor(nv, nt, TringTvor::M_PerTriangle,
			 TringTvor::M_None, TringTvor::M_PerTriangle);

  {
    l = next_oolite_dat_line(f, comment_re);
    if (sscanf(l, "VERTEX") != 0) throw _eh + "Expect VERTEX";

    Float_t *vv = mTTvor->Verts();
    for (Int_t i = 0; i < nv; ++i, vv += 3)
    {
      l = next_oolite_dat_line(f, comment_re);
      if (sscanf(l, "%f, %f, %f", vv, vv+1, vv+2) != 3) throw _eh + "Expect 3 floats for VERTEX";
    }
  }

  {
    l = next_oolite_dat_line(f, comment_re);
    if (sscanf(l, "FACES") != 0) throw _eh + "Expect FACES";

    Int_t   *tt = mTTvor->Trings();
    Float_t *nn = mTTvor->TringNorms();
    Int_t    num_verts;
    Int_t    xxx;
    for (Int_t i = 0; i < nt; ++i, tt += 3, nn += 3)
    {
      l = next_oolite_dat_line(f, comment_re);
      if (sscanf(l, "%d,%d,%d, %f,%f,%f, %d, %d,%d,%d", &xxx, &xxx, &xxx,
		 nn, nn+1, nn+2, &num_verts, tt, tt+1, tt+2) != 10)
      {
	throw _eh + "Expect 10 entries for FACES";
      }
      if (num_verts != 3)
      {
	throw _eh + "Always expect 3 vertices in a face.";
      }

      if (invert_triangles)
      {
	swap(*(tt+1), *(tt+2));
	// Inversion is not ok, regenerate them.
	// *nn = - *nn;  *(nn+1) = - *(nn+1); (nn+2) = - *(nn+2);
      }
    }
    if (invert_triangles)
    {
      mTTvor->GenerateTriangleNormals();
    }
  }

  {
    l = next_oolite_dat_line(f, comment_re);
    if (sscanf(l, "TEXTURES") != 0) throw _eh + "Expect TEXTURES";

    Float_t  sx, sy;
    Float_t *tex = mTTvor->TringTexs();
    char     file[256];
    for (Int_t i = 0; i < nt; ++i, tex += 6)
    {
      l = next_oolite_dat_line(f, comment_re);
      // For fun, there are no commas in TEXTURES section.
      if (sscanf(l, "%s %f %f %f %f %f %f %f %f", file, &sx, &sy,
		 tex, tex+1, tex+2, tex+3, tex+4, tex+5) != 9)
      {
	throw _eh + "Expect 9 entries for TEXTURES";
      }
      if (sx != 1.0f) { sx = 1.0f/sx; tex[0] *= sx; tex[2] *= sx; tex[4] *= sx; }
      if (sy != 1.0f) { sy = 1.0f/sy; tex[1] *= sy; tex[3] *= sy; tex[5] *= sy; }

      if (invert_triangles)
      {
	swap(*(tex+2), *(tex+4));
	swap(*(tex+3), *(tex+5));
      }
    }
  }

  {
    l = next_oolite_dat_line(f, comment_re);
    if (sscanf(l, "END") != 0) throw _eh + "Expect END";
  }

  f.close();
}


/**************************************************************************/
// POV export
/**************************************************************************/

void TriMesh::ExportPovMesh(const char* fname, Bool_t smoothp)
{
  static const Exc_t _eh("TriMesh::ExportPovMesh ");

  assert_tvor(_eh);

  ofstream file(fname);
  mTTvor->ExportPovMesh(file, smoothp);
}


//==============================================================================
// Simple Tvors
//==============================================================================

void TriMesh::make_tetra(Int_t vo, Int_t to, Float_t l1, Float_t l2,
                         Float_t z, Float_t w, Float_t h)
{
  TringTvor& C = *mTTvor;

  w *= 0.5f;

  C.SetVertex(vo + 0, -l2,  w, z);
  C.SetVertex(vo + 1,  l1,  0, z);
  C.SetVertex(vo + 2, -l2, -w, z);
  C.SetVertex(vo + 3,   0,  0, z+h);

  C.SetTriangle(to + 0, vo + 0, vo + 1, vo + 2);
  C.SetTriangle(to + 1, vo + 0, vo + 3, vo + 1);
  C.SetTriangle(to + 2, vo + 1, vo + 3, vo + 2);
  C.SetTriangle(to + 3, vo + 0, vo + 2, vo + 3);
}

void TriMesh::make_tetra_blade(Int_t vo, Int_t to,
                               const Float_t* org, const Float_t* dir,
                               Float_t w, Float_t h)
{
  TringTvor& C = *mTTvor;

  Opcode::Point up(0, 0, 1);
  Opcode::Point right;
  TMath::NormCross(dir, (Float_t*) up, (Float_t*)right);

  right *= 0.5f * w;
  up    *= h;

  Opcode::Point sec(org); sec += dir;

  C.SetVertex(vo + 0, sec + right);
  C.SetVertex(vo + 1, org);
  C.SetVertex(vo + 2, sec - right);
  C.SetVertex(vo + 3, sec + up);

  C.SetTriangle(to + 0, vo + 0, vo + 1, vo + 2);
  C.SetTriangle(to + 1, vo + 0, vo + 3, vo + 1);
  C.SetTriangle(to + 2, vo + 1, vo + 3, vo + 2);
  C.SetTriangle(to + 3, vo + 0, vo + 2, vo + 3);
}

void TriMesh::make_cubus(Int_t vo, Int_t to,
                         Float_t x0, Float_t y0, Float_t z0,
                         Float_t a,  Float_t b,  Float_t c)
{
  TringTvor& C = *mTTvor;

  C.SetVertex(vo + 0, x0,     y0,     z0);
  C.SetVertex(vo + 1, x0,     y0 + b, z0);
  C.SetVertex(vo + 2, x0 + a, y0 + b, z0);
  C.SetVertex(vo + 3, x0 + a, y0,     z0);
  C.SetVertex(vo + 4, x0,     y0,     z0 + c);
  C.SetVertex(vo + 5, x0,     y0 + b, z0 + c);
  C.SetVertex(vo + 6, x0 + a, y0 + b, z0 + c);
  C.SetVertex(vo + 7, x0 + a, y0,     z0 + c);

  C.SetTriangle(to +  0, vo + 0, vo + 1, vo + 2);
  C.SetTriangle(to +  1, vo + 0, vo + 2, vo + 3);
  C.SetTriangle(to +  2, vo + 0, vo + 4, vo + 1);
  C.SetTriangle(to +  3, vo + 1, vo + 4, vo + 5);
  C.SetTriangle(to +  4, vo + 1, vo + 5, vo + 2);
  C.SetTriangle(to +  5, vo + 2, vo + 5, vo + 6);
  C.SetTriangle(to +  6, vo + 2, vo + 6, vo + 3);
  C.SetTriangle(to +  7, vo + 6, vo + 7, vo + 3);
  C.SetTriangle(to +  8, vo + 3, vo + 7, vo + 0);
  C.SetTriangle(to +  9, vo + 7, vo + 4, vo + 0);
  C.SetTriangle(to + 10, vo + 4, vo + 7, vo + 5);
  C.SetTriangle(to + 11, vo + 7, vo + 6, vo + 5);
}

//------------------------------------------------------------------------------

Int_t TriMesh::fill_edge_map(hEdge_t& edge_map, Int_t label)
{
  return fill_edge_map(mTTvor->NTrings(), mTTvor->Trings(), edge_map, label);
}

Int_t TriMesh::fill_edge_map(Int_t n_triangles, Int_t* triangles, hEdge_t& edge_map, Int_t label)
{
  Int_t edge_cnt = 0;

  for (Int_t t = 0; t < n_triangles; ++t)
  {
    pair<hEdge_i, bool> res;
    res = edge_map.insert(make_pair(Edge(triangles[0], triangles[1]), label));
    if (res.second) { ++edge_cnt; ++label; }
    res = edge_map.insert(make_pair(Edge(triangles[1], triangles[2]), label));
    if (res.second) { ++edge_cnt; ++label; }
    res = edge_map.insert(make_pair(Edge(triangles[2], triangles[0]), label));
    if (res.second) { ++edge_cnt; ++label; }
    triangles += 3;
  }

  return edge_cnt;
}

//------------------------------------------------------------------------------

void TriMesh::extrude_triangle(Int_t ti, Float_t h)
{
  // Extrude triangle with index 'ti' and place apex of the new tetra
  // at height 'h' above the triangle's cog.

  TringTvor& C = *mTTvor;

  Float_t norm[3], cog[3];

  C.CalculateTriangleNormalAndCog(ti, norm, cog);

  extrude_triangle(ti, cog[0] + h*norm[0], cog[1] + h*norm[1], cog[2] + h*norm[2]);
}

void TriMesh::extrude_triangle(Int_t ti, Float_t x, Float_t y, Float_t z)
{
  // Extrude triangle with index 'ti' and place apex of the new tetra
  // at the given position.

  TringTvor& C = *mTTvor;

  Int_t nvi = C.AddVertices(1);
  C.SetVertex(nvi, x, y, z);

  Int_t nti = C.AddTriangles(2);

  Int_t* t = C.Triangle(ti);

  C.SetTriangle(nti,     t[1], t[2], nvi);
  C.SetTriangle(nti + 1, t[2], t[0], nvi);
  t[2] = nvi;
}

//------------------------------------------------------------------------------

void TriMesh::colorize_trings_std()
{
  // Colorize the triangles in a standard way.
  // Downwards - blue, otherwise - green.
  // It is assumed that the triangle normals have been calculated.

  const Int_t nTrings = mTTvor->mNTrings;

  const Opcode::Point down(0,0,-1);
  const Opcode::Point back(-1,0,0);

  const Float_t limit = 0.5 * TMath::Sqrt(3);

  for (Int_t t = 0; t < nTrings; ++t)
  {
    Float_t* N = mTTvor->TriangleNormal(t);
    if ((down | N) > limit)
      mTTvor->SetTriangleColor(t, 0, 0, 255);
    else if ((back | N) > limit)
      mTTvor->SetTriangleColor(t, 255, 0, 0);
    else
      mTTvor->SetTriangleColor(t, 0, 255, 0, 0);
  }
}

void TriMesh::colorize_trings_single(UChar_t r, UChar_t g, UChar_t b, UChar_t a)
{
  // Colorize the triangles with given color.

  const Int_t nTrings = mTTvor->mNTrings;

  for (Int_t t = 0; t < nTrings; ++t)
  {
    mTTvor->SetTriangleColor(t, r, g, b, a);
  }
}

//==============================================================================

void TriMesh::MakeTetrahedron(Float_t l1, Float_t l2, Float_t w, Float_t h)
{
  // l1 ~ fwd length; l2 ~ bck length; w ~ width at the end; h ~ height;

  delete mTTvor;
  mTTvor = new TringTvor(4, 4, false, true, false);

  make_tetra(0, 0, l1, l2, 0, w, h);

  mTTvor->GenerateTriangleNormals();
  colorize_trings_std();
  mTTvor->SetTriangleColor(3, 255, 0, 0);

  Stamp(FID());
}

void TriMesh::MakeTetraFlyer(Float_t l1, Float_t l2, Float_t w, Float_t h,
                             Float_t wing_l1, Float_t wing_l2, Float_t wing_z,
                             Float_t wing_w,  Float_t wing_h)
{
  // l1 ~ fwd length; l2 ~ bck length; w ~ width at the end; h ~ height;

  delete mTTvor;
  mTTvor = new TringTvor(8, 8, false, true, false);

  make_tetra(0, 0, l1, l2, 0, w, h);
  make_tetra(4, 4, wing_l1, wing_l2, wing_z, wing_w, wing_h);

  mTTvor->GenerateTriangleNormals();
  colorize_trings_std();
  mTTvor->SetTriangleColor(3, 255, 0, 0);

  Stamp(FID());
}

void TriMesh::MakeTetraChopper(Float_t l1, Float_t l2, Float_t l3, Float_t l4,
                               Float_t w, Float_t h,
                               Float_t wing_l1, Float_t wing_l2,
                               Float_t wing_w,  Float_t wing_h)
{
  delete mTTvor;
  mTTvor = new TringTvor(4, 4, false, true, false);

  make_tetra(0, 0, l1, l2, 0, w, h);

  extrude_triangle(3, -l3, 0, 0.5f  * h);
  extrude_triangle(3, -l4, 0, 0.75f * h);
  extrude_triangle(3, -l3, 0, 0.f * h);

  Float_t dir[3] = { wing_l1, wing_l2, 0 };
  Int_t   nv, nt;

  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, wing_w, wing_h);

  dir[1] = - dir[1];
  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, wing_w, wing_h);

  dir[0] = - dir[0];
  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, wing_w, wing_h);

  dir[1] = - dir[1];
  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, wing_w, wing_h);

  mTTvor->GenerateTriangleNormals();
  colorize_trings_std();
  mTTvor->SetTriangleColor(3, 255, 0, 0);

  Stamp(FID());
}

void TriMesh::MakeTetraMark(Float_t r0, Float_t h0,
			    Float_t r1, Float_t w1, Float_t h1)
{
  delete mTTvor;
  mTTvor = new TringTvor(4, 4, false, false, false);

  make_tetra(0, 0, 2*r0/3, r0/3, 0, r0, h0);

  Float_t dir[3] = { r1, 0, 0 };
  Int_t   nv, nt;

  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, w1, h1);

  dir[0] = - dir[0];
  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, w1, h1);

  dir[1] = dir[0]; dir[0] = 0;
  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, w1, h1);

  dir[1] = - dir[1];
  nv = mTTvor->AddVertices(4);
  nt = mTTvor->AddTriangles(4);
  make_tetra_blade(nv, nt, mTTvor->Vertex(3), dir, w1, h1);

  mTTvor->GenerateTriangleNormals();

  Stamp(FID());
}

void TriMesh::MakeBox(Float_t a, Float_t b, Float_t c)
{
  // Create a box centered on 0 in x and y directions and going from 0
  // to c in z.

  delete mTTvor;
  mTTvor = new TringTvor(8, 12, false, false, false);

  make_cubus(0, 0, -0.5*a, -0.5*b, 0, a, b, c);

  mTTvor->GenerateTriangleNormals();

  Stamp(FID());
}

void TriMesh::MakeIcosahedron()
{
  delete mTTvor;
  mTTvor = new TringTvor(12, 20, false, false, false);
  TringTvor& T = *mTTvor;

  // X = sqrt(sqrt(5)+1)/sqrt(2*sqrt(5)) 
  // Y = sqrt(2)/sqrt(5+sqrt(5))
  const Double_t X = 0.850650808352039932181540497063011072240401406;
  const Double_t Y = 0.525731112119133606025669084847876607285497935;
  const Double_t Z = 0;

  T.SetVertex( 0, +Z, +X, -Y);    T.SetVertex( 1, +X, +Y, +Z);
  T.SetVertex( 2, +Y, +Z, -X);    T.SetVertex( 3, +Y, +Z, +X);
  T.SetVertex( 4, +X, -Y, +Z);    T.SetVertex( 5, +Z, +X, +Y);
  T.SetVertex( 6, -Y, +Z, +X);    T.SetVertex( 7, +Z, -X, -Y);
  T.SetVertex( 8, -X, +Y, +Z);    T.SetVertex( 9, -Y, +Z, -X);
  T.SetVertex(10, -X, -Y, +Z);    T.SetVertex(11, +Z, -X, +Y);

  T.SetTriangle( 0,  0,  1,  2);  T.SetTriangle( 1,  3,  4,  1);
  T.SetTriangle( 2,  5,  6,  3);  T.SetTriangle( 3,  7,  2,  4);
  T.SetTriangle( 4,  5,  8,  6);  T.SetTriangle( 5,  7,  9,  2);
  T.SetTriangle( 6,  5,  0,  8);  T.SetTriangle( 7,  7, 10,  9);
  T.SetTriangle( 8,  5,  1,  0);  T.SetTriangle( 9, 11, 10,  7);
  T.SetTriangle(10,  5,  3,  1);  T.SetTriangle(11, 11,  6, 10);
  T.SetTriangle(12, 11,  3,  6);  T.SetTriangle(13,  9, 10,  8);
  T.SetTriangle(14, 11,  4,  3);  T.SetTriangle(15,  6,  8, 10);
  T.SetTriangle(16, 11,  7,  4);  T.SetTriangle(17,  2,  1,  4);
  T.SetTriangle(18,  0,  9,  8);  T.SetTriangle(19,  0,  2,  9);

  Stamp(FID());
}

void TriMesh::NormalizeVertices()
{
  static const Exc_t _eh("TriMesh::NormalizeVertices ");

  assert_tvor(_eh);
  TringTvor &T = *mTTvor;

  Float_t *v = T.Verts();
  Int_t    N = 3*T.mNVerts;
  for (Int_t i = 0; i < N; i += 3, v += 3)
  {
    TMath::Normalize(v);
  }
}

void TriMesh::ScaleVertices(Float_t s)
{
  static const Exc_t _eh("TriMesh::ScaleVertices ");

  assert_tvor(_eh);
  TringTvor &T = *mTTvor;

  Float_t *v = T.Verts();
  Int_t    N = 3*T.mNVerts;
  for (Int_t i = 0; i < N; ++i)
  {
    v[i] *= s;
  }
}


/**************************************************************************/
// Vertex algorithms
/**************************************************************************/

TriMesh::EdgeData::EdgeData() :
  fV1(-1), fV2(-1), fT1(-1), fT2(-1),
  fDistance(0), fDh(0),   fAngle(0),
  fSurface(0),  fSpr1(0), fSpr2(0)
{}

namespace
{
struct edge_sort_cmp
{
  const vector<TriMesh::EdgeData>& edvec;

  edge_sort_cmp(const vector<TriMesh::EdgeData>& ev) : edvec(ev) {}

  bool operator()(const Int_t& e1, const Int_t& e2)
  { return edvec[e1].fAngle < edvec[e2].fAngle; }
};
}

void TriMesh::BuildVertexConnections()
{
  static const Exc_t _eh("TriMesh::BuildVertexConnections ");

  assert_tvor(_eh);

  const Int_t nVerts  = mTTvor->mNVerts;
  const Int_t nTrings = mTTvor->mNTrings;
  Int_t       nConns  = 0, nEdges = 0;

  vector<Int_t> nconn_per_vertex(nVerts);
  hEdge_t       edge_map;

  const Int_t vP[3][2] = { { 0, 1 }, { 1, 2 }, { 2, 0 } };

  // Initialize edge-map, count edges and connections.
  for (Int_t tring = 0; tring < nTrings; ++tring)
  {
    Int_t *vts = mTTvor->Triangle(tring);
    for (Int_t e = 0; e < 3; ++e)
    {
      const Int_t v1 = vts[vP[e][0]], v2 = vts[vP[e][1]];
      if (edge_map.insert(make_pair(Edge(v1, v2), nEdges)).second)
      {
        ++nEdges;
        ++nconn_per_vertex[v1];
        ++nconn_per_vertex[v2];
        nConns += 2;
      }
    }
  }

  printf("%snEdges=%d, nConns=%d.\n", _eh.Data(), nEdges, nConns);

  // Allocate arrays, now their exact sizes are known
  vector<VertexData> vertex_data_vec(nVerts);
  mVDataVec.swap(vertex_data_vec);

  vector<TriMesh::EdgeData> edge_data_vec(nEdges);
  mEDataVec.swap(edge_data_vec);

  vector<Int_t> edge_curs_vec(nConns);
  mECursVec.swap(edge_curs_vec);


  // Loop over vertices, init edge-cursors
  {
    Int_t *edge_cur = & mECursVec[0];
    for (Int_t v = 0; v < nVerts; ++v)
    {
      mVDataVec[v].fEdgeArr = edge_cur;
      edge_cur += nconn_per_vertex[v];
    }
  }

  // Loop over triangles, setup basic edge data
  {
    for (Int_t tring = 0; tring < nTrings; ++tring)
    {
      Int_t *vts = mTTvor->Triangle(tring);
      for (Int_t eti = 0; eti < 3; ++eti)
      {
        const Int_t v1 = vts[vP[eti][0]], v2 = vts[vP[eti][1]];
        const Int_t ei = edge_map.find(Edge(v1, v2))->second;
        EdgeData& ed = mEDataVec[ei];
        if (ed.fV1 == -1)
        {
          ed.fV1 = v1;  ed.fV2 = v2;  ed.fT1 = tring;
          mVDataVec[v1].insert_edge(ei);
          mVDataVec[v2].insert_edge(ei);
        }
        else
        {
          ed.fT2 = tring;
        }
      }
    }
  }

  // From this point on the edge_map is no longer used so that the
  // code can be easily moved to a separate function.

  // Loop over triangles, calc and accumulate edge & vertex-data
  {
    const Int_t oE[3]    = { 2, 0, 1 }; // other-edge for spread calculation

    for (Int_t tring = 0; tring < nTrings; ++tring)
    {
      using namespace Opcode;

      Int_t* vts = mTTvor->Triangle(tring);
      const Point* v[3] = { (Point*) mTTvor->Vertex(vts[0]), (Point*) mTTvor->Vertex(vts[1]), (Point*) mTTvor->Vertex(vts[2]) };
      Point e[3]; e[0].Sub(*v[1], *v[0]); e[1].Sub(*v[2], *v[1]); e[2].Sub(*v[0], *v[2]);

      Float_t tri_surfo3 = (e[0] ^ e[2]).Magnitude() / 6.0f;
      Float_t mags[3]    = { e[0].Magnitude(), e[1].Magnitude(), e[2].Magnitude() };
      Float_t spreads[3];  // half-angles per vertex
      for (Int_t i = 0; i < 3; ++i)
        spreads[i] = 0.5f * acosf(-(e[i] | e[oE[i]])/(mags[i]*mags[oE[i]]));

      for (Int_t eti = 0; eti < 3; ++eti)
      {
        const Int_t v1 = vts[vP[eti][0]], v2 = vts[vP[eti][1]];
        VertexData &vd = mVDataVec[v1];
        EdgeData&   ed = find_edge(vd, v1, v2);

        if (ed.fDistance == 0)
        {
          ed.fDistance = e[eti].Magnitude();
          Point mid_edge;
          mid_edge.Mac(*v[eti], e[eti], 0.5f);
          Point dirs[3];
          mParaSurf->pos2fghdir(mid_edge, dirs[0], dirs[1], dirs[2]);
          ed.fDh = e[eti] | dirs[2];
          ed.fAngle = atan2f(e[eti] | dirs[0], e[eti] | dirs[0]);
          if (ed.fAngle < 0)
            ed.fAngle += PI;
        }
        ed.fSurface  += tri_surfo3;
        if (ed.fV1 == v1)
          ed.fSpr1 += spreads[vP[eti][0]], ed.fSpr2 += spreads[vP[eti][1]];
        else
          ed.fSpr2 += spreads[vP[eti][0]], ed.fSpr1 += spreads[vP[eti][1]];

        vd.fSurface += tri_surfo3;
        vd.fSpread  += 2.0f * spreads[eti];
      }
    }

    // For each vertex, sort edge cursors by their angle.
    for (Int_t v = 0; v < nVerts; ++v)
    {
      VertexData& vd = mVDataVec[v];
      sort(vd.fEdgeArr, vd.fEdgeArr + vd.fNEdges, edge_sort_cmp(mEDataVec));
    }
  }
}

void TriMesh::AssertVertexConnections()
{
  if (! HasVertexConnections())
    BuildVertexConnections();
}

Bool_t TriMesh::HasVertexConnections()
{
  return mTTvor != 0 && (Int_t) mVDataVec.size() == mTTvor->mNVerts;
}

//******************************************************************************

Bool_t TriMesh::FindPointFromFGH(const Float_t fgh[3], Bool_t absolute_h,
				 Float_t xyz_out[3], Float_t* h_out, Int_t* triangle_idx)
{
  // Find world-point corresponding to the passed fgh coordinates and return it
  // in the xyz_out array.
  //
  // Flag absolute_h determines how the input h-coordinate is interpreted and
  // also what will be returned in h_out:
  //   true  - h is absolute, h_out is relative to the surface;
  //   false - h is relative to the surface, h_out is absolute h.
  //
  // trianlge_idx (optional) is index of the triangle on the surface that was
  // hit by the ray used to find the surface point. If you need vertex index as
  // well, call 'Int_t FindClosestVertex(xyz_out, triangle_idx)'.
  //
  // Returns false if ray-mesh intersection fails.

  static const Exc_t _eh("TriMesh::FindPointFromFGH ");

  assert_parasurf(_eh);

  Opcode::Point& xyz = *(Opcode::Point*)xyz_out;
  mParaSurf->fgh2pos(fgh, xyz);

  Opcode::RayCollider    RC;
  RC.SetFirstContact(false);  // true to only take first hit (not closest!)
  RC.SetClosestHit(true);     // to keep the closes hit only
  Opcode::CollisionFaces CF;
  RC.SetDestination(&CF);

  Opcode::Ray R;
  Float_t ray_offset = mParaSurf->pos2hray(xyz, R);

  bool cs = RC.Collide(R, *mOPCModel, 0, (UInt_t*) triangle_idx);
  if (cs && CF.GetNbFaces() == 1)
  {
      const Opcode::CollisionFace& cf = CF.GetFaces()[0];
      if (absolute_h)
      {
        if (h_out) *h_out = cf.mDistance - ray_offset;
      }
      else
      {
        xyz.TMac(R.mDir, cf.mDistance - ray_offset - fgh[2]);
        if (h_out) *h_out = mParaSurf->GetMaxH() + mParaSurf->GetEpsilon() - cf.mDistance + fgh[2];
      }
      if (triangle_idx) *triangle_idx = cf.mFaceID;
      return true;
  }
  else
  {
    ISwarn(_eh + RC.CollideInfo(cs, R));
    return false;
  }
}

Bool_t TriMesh::FindPointFromXYZH(const Float_t xyz_in[3], Float_t h_in,
				  Float_t xyz_out[3], Float_t* h_out, Int_t* triangle_idx)
{
  // Given world-point xyz_in and relative height h_in, find the
  // corresponding point xyz_out at specified height. The point is
  // only translated in vertical direction. xyz_in and xyz_out can
  // point to the same location.
  //
  // h_out (if non-null) is set to relative height of the xyz_in point.
  //
  // trianlge_idx (optional) is index of the triangle on the surface that was
  // hit by the ray used to find the surface point. If you need vertex index as
  // well, call 'Int_t FindClosestVertex(xyz_out, triangle_idx)'.
  //
  // Returns false if ray-mesh intersection fails.

  static const Exc_t _eh("TriMesh::FindPointFromXYZH ");

  assert_parasurf(_eh);

  Opcode::Point xyz(xyz_in);

  Opcode::RayCollider    RC;
  RC.SetClosestHit(true);
  RC.SetTemporalCoherence(true);
  Opcode::CollisionFaces CF;
  RC.SetDestination(&CF);

  Opcode::Ray R;
  Float_t ray_offset = mParaSurf->pos2hray(xyz, R);

  bool cs = RC.Collide(R, *mOPCModel, 0, (UInt_t*) triangle_idx);
  if (cs && CF.GetNbFaces() == 1)
  {
    const Opcode::CollisionFace& cf = CF.GetFaces()[0];

    xyz.TMac(R.mDir, cf.mDistance - ray_offset - h_in);

    if (h_out) *h_out = cf.mDistance - ray_offset;

    xyz_out[0] = xyz.x; xyz_out[1] = xyz.y; xyz_out[2] = xyz.z;

    if (triangle_idx) *triangle_idx = cf.mFaceID;
    return true;
  }
  else
  {
    if (triangle_idx) *triangle_idx = Opcode::OPC_INVALID_ID;
    // ISwarn(_eh + RC.CollideInfo(cs, R));
    return false;
  }
}

//******************************************************************************

Int_t TriMesh::FindClosestVertex(Int_t triangle, const Float_t xyz[3],
                                 Float_t* sqr_dist)
{
  // Find vertex of triangle that is closest to point given by xyz.
  // If sqr_dist is passed, it is filled with square distance of the returned
  // vertex.
  // Trianlge index is not checked, neither is tvor's presence.

  TringTvor& TT = *mTTvor;

  Int_t  *t     = TT.Triangle(triangle);
  Float_t minsq = TT.SqrDistanceToVertex(t[0], xyz);
  Int_t   vi    = t[0];
  for (int i=1; i<3; ++i) {
    const Float_t d = TT.SqrDistanceToVertex(t[i], xyz);
    if (d < minsq) {
      minsq = d;
      vi    = t[1];
    }
  }
  if (sqr_dist) *sqr_dist = minsq;
  return vi;
}

//******************************************************************************

namespace
{
Bool_t check_uv(Opcode::Point& uv, Opcode::Point& duv, Float_t t, Bool_t printp=false)
{
  static const Float_t lim0 = -0.005;
  static const Float_t lim1 =  1.01;

  Float_t u  = uv.x + t*duv.x;
  Float_t v  = uv.y + t*duv.y;
  Float_t s  = u + v;

  if (printp)
    printf("  %f, u=%f, s=%f, v=%f\n", t, u, s, v);

  return (u > lim0 && u < lim1 && v > lim0 && v < lim1 && s > lim0 && s < lim1);
}
}

Bool_t TriMesh::FindTriangleExitPoint(Int_t triangle, const Float_t xyz[3], const Float_t dir[3],
				      Float_t xyz_out[3], Int_t* next_triangle)
{
  // Given triangle, position xyz and direction dir, find the exit point from
  // the triangle.
  // If next_triangle is non-null, the triangle bordering the crossed edge is
  // returned there.
  //
  // There are very many ways this can go wrong ... still working on the
  // interface and the return value.
  // 1. The direction is orthogonal to triangle normal.
  // 2. All intersection points are either outside of triangle or at negative
  //    times.
  // 3. Next triangle index is -1 - the end of the world is hit.
  //
  // Now, a hack was added to also succeed when the triangle edge has just
  // been passed: the function is re-entered with the new triangle. Again this
  // can go wrong, so a set of triangles that have already been tried is
  // cross-checked before re-entry to avoid infinite loop.

  static const Exc_t _eh("TriMesh::FindTriangleExitPoint ");

  static const Int_t s_idcs[3][2] = { {0,1}, {1,2}, {2,0} };

  if (! HasVertexConnections())
    throw _eh + "No vertex connections on '" + mName + "'.";

  using namespace Opcode;

  TringTvor& TT = *mTTvor;

  std::set<Int_t> tried_triangles;
reentry:

  Int_t* t = TT.Triangle(triangle);
  Point V[3] = { TT.Vertex(t[0]), TT.Vertex(t[1]), TT.Vertex(t[2]) };

  // Define triangle coords
  Point e1; e1.Sub(V[1], V[0]);
  Point e2; e2.Sub(V[2], V[0]);
  Float_t e1sq = e1.SquareMagnitude();
  Float_t e2sq = e2.SquareMagnitude();
  Float_t d    = e1 | e2;

  // Calculate u,v coords of both points.
  Point uv;
  Point p(xyz); p -= V[0];
  Float_t e1p  = e1 | p;
  Float_t e2p  = e2 | p;
  uv.x = (e1p * e2sq - e2p * d) / (e1sq * e2sq - d * d);
  uv.y = (e2p - uv.x * d) / e2sq;
  uv.z = 0;

  Point duv;
  Point q(dir);
  Float_t e1q  = e1 | q;
  Float_t e2q  = e2 | q;
  duv.x = (e1q * e2sq - e2q * d) / (e1sq * e2sq - d * d);
  duv.y = (e2q - duv.x * d) / e2sq;
  duv.z = 0;

  Float_t duv_norm = duv.NormalizeAndReport();
  if (duv_norm > 1e-6)
  {
    // The direction has sufficient u,v components.

    Float_t t_max = -1, t_neg_max = -1;
    Int_t   t_i   = -1, t_neg_i   =  0;

    Float_t t_u  = - uv.y / duv.y;
    if (check_uv(uv, duv, t_u) && t_u > t_max) {
      t_max = t_u; t_i = 0;
    }
    if (t_u < 0 && t_u > t_neg_max) {
      t_neg_max = t_u; t_neg_i = 0;
    }

    Float_t t_uv = (1.0f - uv.x - uv.y) / (duv.x + duv.y);
    if (check_uv(uv, duv, t_uv) && t_uv > t_max) {
      t_max = t_uv; t_i = 1;
    }
    if (t_uv < 0 && t_uv > t_neg_max) {
      t_neg_max = t_uv; t_neg_i = 1;
    }

    Float_t t_v  = - uv.x / duv.x;
    if (check_uv(uv, duv, t_v) && t_v > t_max) {
      t_max = t_v; t_i = 2;
    }
    if (t_v < 0 && t_v > t_neg_max) {
      t_neg_max = t_v; t_neg_i = 2;
    }

    if (t_i == -1)
    {
      // The line is not crossing the triangle.

      // Some old, high-quality debug crap.
      // printf("No cross, times: %f, %f, %f; t_max=%f, t_i=%d;t_neg_max=%f, t_neg_i=%d\n",
      //        t_u, t_uv, t_v, t_max, t_i, t_neg_max, t_neg_i);
      // check_uv(uv, duv, t_u, true);
      // check_uv(uv, duv, t_uv, true);
      // check_uv(uv, duv, t_v, true);

      // There is still the chance that we just crossed it ... so look
      // for negative time closest to zero (the limit in time is -1).
      if (t_neg_i != -1)
      {
	Int_t vi_0 = t[s_idcs[t_neg_i][0]], vi_1 = t[s_idcs[t_neg_i][1]];
	triangle = find_edge(vi_0, vi_1).right_triangle(vi_0);
	// printf("  new_tring=%u ... %s reentering - wish me luck :)\n",
	//        triangle, tried_triangles.find(triangle) == tried_triangles.end() ? "yes" : "no");
	if (triangle != -1 && tried_triangles.find(triangle) == tried_triangles.end())
	{
	  tried_triangles.insert(triangle);
	  goto reentry;
	}
      }
      return false;
    }

    Point o(xyz);
    o.TMac2(e1, duv.x*t_max, e2, duv.y*t_max);
    xyz_out[0] = o.x;
    xyz_out[1] = o.y;
    xyz_out[2] = o.z;

    if (next_triangle)
    {
      Int_t vi_0 = t[s_idcs[t_i][0]], vi_1 = t[s_idcs[t_i][1]];
      *next_triangle = find_edge(vi_0, vi_1).right_triangle(vi_0);
    }

    return true;
  }
  else
  {
    // The direction is perpendicular to the triangle.
    // Cross to the "other side" of the triangle - to the most distant
    // edge center.

    printf("Degenerate crap, duv_norm=%f; uv=%f,%f\n", duv_norm, uv.x, uv.y);

    // find closest vertex, take opposite edge.

    return false;
  }
}

//******************************************************************************

Int_t TriMesh::VisitVertices(Int_t vertex, VertexVisitor& vertex_visitor,
                             set<Int_t>& visited_vertices,
                             set<Int_t>& accepted_vertices)
{
  // Visits vertex by calling VertexVisitor::VisitVertex(vertex). If
  // this returns true, the neighbouring vertices are visited in order
  // specified in the vertex edge-array.
  //
  // Set visited_vertices holds already visited vertices, they will
  // not be visited twice.
  //
  // Set accepted_vertices holds the vertices for which the visitor
  // returned true.
  //
  // Returns the maximum depth of recursion.

  Int_t max_depth = 0;

  visited_vertices.insert(vertex);

  if (vertex_visitor.VisitVertex(vertex))
  {
    accepted_vertices.insert(vertex);

    const VertexData& vd = mVDataVec[vertex];
    for (Int_t e = 0; e < vd.n_edges(); ++e)
    {
      Int_t next_vertex = mEDataVec[vd.edge(e)].other_vertex(vertex);

      if (next_vertex >= 0 &&
          visited_vertices.find(next_vertex) == visited_vertices.end())
      {
        Int_t depth = VisitVertices(next_vertex, vertex_visitor,
                                    visited_vertices, accepted_vertices);
        if (depth > max_depth)
          max_depth = depth;
      }
    }
    ++max_depth;
  }

  return max_depth;
}

//==============================================================================
// TriMesh colorizers
//==============================================================================

void TriMesh::ColorByCoord(RGBAPalette* pal, ZGlass* carr_src_lens,
			   Int_t axis, Float_t fac, Float_t offset)
{
  static const Exc_t _eh("TriMesh::ColorByCoord ");

  if (!pal)                 throw _eh + "Palette argument null.";
  if (axis < 0 || axis > 2) throw _eh + "illegal axis.";
  assert_tvor(_eh);

  TriMeshColorArraySource *carr_src =
    TriMeshColorArraySource::CastLens(_eh, carr_src_lens, true);
  if (carr_src == 0) carr_src = this;
  carr_src->AssertVertexColorArray();

  UChar_t *VCA = carr_src->GetVertexColorArray();
  UChar_t *TCA = carr_src->GetTriangleColorArray();

  TringTvor &TT = *mTTvor;
  TT.AssertBoundingBox();
  Float_t min = TT.mMinMaxBox[axis];
  Float_t max = TT.mMinMaxBox[axis + 3];
  Float_t dlt = max - min;

  pal->SetMinFlt(min);
  pal->SetMaxFlt(max);

  Float_t* V = TT.Verts();
  UChar_t* C = VCA;
  for (Int_t i=0; i<TT.mNVerts; ++i, V+=3, C+=4)
    pal->ColorFromValue(min + (V[axis]-min)*fac + dlt*offset, C);

  if (TCA)
  {
    TT.GenerateTriangleColorsFromVertexColors(VCA, TCA);
  }

  carr_src->ColorArraysModified();
}

void TriMesh::ColorByNormal(RGBAPalette* pal, ZGlass* carr_src_lens,
			    Int_t axis, Float_t min, Float_t max)
{
  static const Exc_t _eh("TriMesh::ColorByNormal ");

  if (!pal)                 throw (_eh + "Palette argument null.");
  if (axis < 0 || axis > 2) throw (_eh + "illegal axis.");
  assert_tvor(_eh);

  TriMeshColorArraySource *carr_src =
    TriMeshColorArraySource::CastLens(_eh, carr_src_lens, true);
  if (carr_src == 0) carr_src = this;
  carr_src->AssertVertexColorArray();

  UChar_t *VCA = carr_src->GetVertexColorArray();
  UChar_t *TCA = carr_src->GetTriangleColorArray();

  pal->SetMinFlt(min);
  pal->SetMaxFlt(max);

  TringTvor &TT = *mTTvor;
  Float_t   *N  = TT.Norms();
  UChar_t   *C  = VCA;
  for (Int_t i=0; i<TT.mNVerts; ++i, N+=3, C+=4)
  {
    pal->ColorFromValue(N[axis], C);
  }

  if (TCA)
  {
    TT.GenerateTriangleColorsFromVertexColors(VCA, TCA);
  }

  carr_src->ColorArraysModified();
}

//------------------------------------------------------------------------------

void TriMesh::ColorByParaSurfCoord(RGBAPalette* pal, ZGlass* carr_src_lens,
				   Int_t axis, Float_t fac, Float_t offset)
{
  static const Exc_t _eh("TriMesh::ColorByParaSurfCoord ");

  if (!pal)                 throw (_eh + "Palette argument null.");
  if (axis < 0 || axis > 2) throw (_eh + "illegal axis.");
  assert_tvor(_eh);
  assert_parasurf(_eh);

  TriMeshColorArraySource *carr_src =
    TriMeshColorArraySource::CastLens(_eh, carr_src_lens, true);
  if (carr_src == 0) carr_src = this;
  carr_src->AssertVertexColorArray();

  UChar_t *VCA = carr_src->GetVertexColorArray();
  UChar_t *TCA = carr_src->GetTriangleColorArray();

  Float_t  fgh[3];

  mParaSurf->GetMinFGH(fgh);
  Float_t min = fgh[axis];
  mParaSurf->GetMaxFGH(fgh);
  Float_t max = fgh[axis];
  Float_t dlt = max - min;

  pal->SetMinFlt(min);
  pal->SetMaxFlt(max);

  TringTvor &TT = *mTTvor;
  Float_t   *V  = TT.Verts();
  UChar_t   *C  = VCA;
  for (Int_t i=0; i<TT.mNVerts; ++i, V+=3, C+=4)
  {
    mParaSurf->pos2fgh(V, fgh);
    pal->ColorFromValue(min + (fgh[axis]-min)*fac + dlt*offset, C);
  }

  if (TCA)
  {
    TT.GenerateTriangleColorsFromVertexColors(VCA, TCA);
  }

  carr_src->ColorArraysModified();
}

void TriMesh::ColorByParaSurfNormal(RGBAPalette* pal, ZGlass* carr_src_lens,
				    Int_t axis, Float_t min, Float_t max)
{
  static const Exc_t _eh("TriMesh::ColorByParaSurfNormal ");

  if (!pal)                 throw (_eh + "Palette argument null.");
  if (axis < 0 || axis > 2) throw (_eh + "illegal axis.");
  assert_tvor(_eh);
  assert_parasurf(_eh);

  TriMeshColorArraySource *carr_src =
    TriMeshColorArraySource::CastLens(_eh, carr_src_lens, true);
  if (carr_src == 0) carr_src = this;
  carr_src->AssertVertexColorArray();

  UChar_t *VCA = carr_src->GetVertexColorArray();
  UChar_t *TCA = carr_src->GetTriangleColorArray();

  pal->SetMinFlt(min);
  pal->SetMaxFlt(max);

  TringTvor &TT = *mTTvor;
  Float_t   *V  = TT.Verts();
  Float_t   *N  = TT.Norms();
  UChar_t   *C  = VCA;
  Float_t    fgh_dirs[3][3];
  for (Int_t i=0; i<TT.mNVerts; ++i, V+=3, N+=3, C+=4)
  {
    mParaSurf->pos2fghdir(V, fgh_dirs[0], fgh_dirs[1], fgh_dirs[2]);
    Float_t dotp = N[0]*fgh_dirs[axis][0] + N[1]*fgh_dirs[axis][1] + N[2]*fgh_dirs[axis][2];
    pal->ColorFromValue(dotp, C);
  }

  if (TCA)
  {
    TT.GenerateTriangleColorsFromVertexColors(VCA, TCA);
  }

  carr_src->ColorArraysModified();
}

//------------------------------------------------------------------------------

void TriMesh::ColorByCoordFormula(RGBAPalette* pal, ZGlass* carr_src_lens,
				  const Text_t* formula, Float_t min, Float_t max)
{
  static const Exc_t _eh("TriMesh::ColorByCoordFormula ");

  if (!pal)    throw (_eh + "Palette argument null.");
  assert_tvor(_eh);

  TriMeshColorArraySource *carr_src =
    TriMeshColorArraySource::CastLens(_eh, carr_src_lens, true);
  if (carr_src == 0) carr_src = this;
  carr_src->AssertVertexColorArray();

  UChar_t *VCA = carr_src->GetVertexColorArray();
  UChar_t *TCA = carr_src->GetTriangleColorArray();

  TringTvor &TT = *mTTvor;
  TT.AssertBoundingBox();
  Float_t* bb = TT.mMinMaxBox;
  TF3 tf3(GForm("TriMesh_CBCF_%d", GetSaturnID()), formula, 0, 0);
  tf3.SetRange(bb[0], bb[3], bb[1], bb[4], bb[2], bb[5]);

  pal->SetMinFlt(min);
  pal->SetMaxFlt(max);

  Float_t* V = TT.Verts();
  UChar_t* C = VCA;
  for (Int_t i=0; i<TT.mNVerts; ++i, V+=3, C+=4)
  {
    pal->ColorFromValue((Float_t) tf3.Eval(V[0], V[1], V[2]), C);
  }

  if (TCA)
  {
    TT.GenerateTriangleColorsFromVertexColors(VCA, TCA);
  }

  carr_src->ColorArraysModified();
}

void TriMesh::ColorByNormalFormula(RGBAPalette* pal, ZGlass* carr_src_lens,
				   const Text_t* formula, Float_t min, Float_t max)
{
  static const Exc_t _eh("TriMesh::ColorByNormalFormula ");

  if (!pal)    throw (_eh + "Palette argument null.");
  assert_tvor(_eh);

  TriMeshColorArraySource *carr_src =
    TriMeshColorArraySource::CastLens(_eh, carr_src_lens, true);
  if (carr_src == 0) carr_src = this;
  carr_src->AssertVertexColorArray();

  UChar_t *VCA = carr_src->GetVertexColorArray();
  UChar_t *TCA = carr_src->GetTriangleColorArray();

  pal->SetMinFlt(min);
  pal->SetMaxFlt(max);

  TF3 tf3(GForm("TriMesh_CBNF_%d", GetSaturnID()), formula, 0, 0);
  tf3.SetRange(-1, 1, -1, 1, -1, 1);

  TringTvor &TT = *mTTvor;
  Float_t   *N  = TT.Norms();
  UChar_t   *C  = VCA;
  for (Int_t i=0; i<TT.mNVerts; ++i, N+=3, C+=4)
  {
    pal->ColorFromValue((Float_t) tf3.Eval(N[0], N[1], N[2]), C);
  }

  if (TCA)
  {
    TT.GenerateTriangleColorsFromVertexColors(VCA, TCA);
  }

  carr_src->ColorArraysModified();
}


//==============================================================================
// TriMesh::VertexVisitorMaxDist
//==============================================================================

TriMesh::VertexVisitorMaxDist::
VertexVisitorMaxDist(TriMesh* m, const Float_t origin[3], Float_t max_dist) :
  VertexVisitor(m),
  mMaxDistSqr(max_dist*max_dist),
  mLastDistSqr(-1)
{
  mOrigin[0] = origin[0]; mOrigin[1] = origin[1]; mOrigin[2] = origin[2];
}

Bool_t TriMesh::VertexVisitorMaxDist::VisitVertex(Int_t vertex)
{
  Float_t* v = mMesh->GetTTvor()->Vertex(vertex);
  Float_t  d[3];
  d[0] = v[0] - mOrigin[0];
  d[1] = v[1] - mOrigin[1];
  d[2] = v[2] - mOrigin[2];
  mLastDistSqr = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
  return (mLastDistSqr <= mMaxDistSqr);
}
 TriMesh.cxx:1
 TriMesh.cxx:2
 TriMesh.cxx:3
 TriMesh.cxx:4
 TriMesh.cxx:5
 TriMesh.cxx:6
 TriMesh.cxx:7
 TriMesh.cxx:8
 TriMesh.cxx:9
 TriMesh.cxx:10
 TriMesh.cxx:11
 TriMesh.cxx:12
 TriMesh.cxx:13
 TriMesh.cxx:14
 TriMesh.cxx:15
 TriMesh.cxx:16
 TriMesh.cxx:17
 TriMesh.cxx:18
 TriMesh.cxx:19
 TriMesh.cxx:20
 TriMesh.cxx:21
 TriMesh.cxx:22
 TriMesh.cxx:23
 TriMesh.cxx:24
 TriMesh.cxx:25
 TriMesh.cxx:26
 TriMesh.cxx:27
 TriMesh.cxx:28
 TriMesh.cxx:29
 TriMesh.cxx:30
 TriMesh.cxx:31
 TriMesh.cxx:32
 TriMesh.cxx:33
 TriMesh.cxx:34
 TriMesh.cxx:35
 TriMesh.cxx:36
 TriMesh.cxx:37
 TriMesh.cxx:38
 TriMesh.cxx:39
 TriMesh.cxx:40
 TriMesh.cxx:41
 TriMesh.cxx:42
 TriMesh.cxx:43
 TriMesh.cxx:44
 TriMesh.cxx:45
 TriMesh.cxx:46
 TriMesh.cxx:47
 TriMesh.cxx:48
 TriMesh.cxx:49
 TriMesh.cxx:50
 TriMesh.cxx:51
 TriMesh.cxx:52
 TriMesh.cxx:53
 TriMesh.cxx:54
 TriMesh.cxx:55
 TriMesh.cxx:56
 TriMesh.cxx:57
 TriMesh.cxx:58
 TriMesh.cxx:59
 TriMesh.cxx:60
 TriMesh.cxx:61
 TriMesh.cxx:62
 TriMesh.cxx:63
 TriMesh.cxx:64
 TriMesh.cxx:65
 TriMesh.cxx:66
 TriMesh.cxx:67
 TriMesh.cxx:68
 TriMesh.cxx:69
 TriMesh.cxx:70
 TriMesh.cxx:71
 TriMesh.cxx:72
 TriMesh.cxx:73
 TriMesh.cxx:74
 TriMesh.cxx:75
 TriMesh.cxx:76
 TriMesh.cxx:77
 TriMesh.cxx:78
 TriMesh.cxx:79
 TriMesh.cxx:80
 TriMesh.cxx:81
 TriMesh.cxx:82
 TriMesh.cxx:83
 TriMesh.cxx:84
 TriMesh.cxx:85
 TriMesh.cxx:86
 TriMesh.cxx:87
 TriMesh.cxx:88
 TriMesh.cxx:89
 TriMesh.cxx:90
 TriMesh.cxx:91
 TriMesh.cxx:92
 TriMesh.cxx:93
 TriMesh.cxx:94
 TriMesh.cxx:95
 TriMesh.cxx:96
 TriMesh.cxx:97
 TriMesh.cxx:98
 TriMesh.cxx:99
 TriMesh.cxx:100
 TriMesh.cxx:101
 TriMesh.cxx:102
 TriMesh.cxx:103
 TriMesh.cxx:104
 TriMesh.cxx:105
 TriMesh.cxx:106
 TriMesh.cxx:107
 TriMesh.cxx:108
 TriMesh.cxx:109
 TriMesh.cxx:110
 TriMesh.cxx:111
 TriMesh.cxx:112
 TriMesh.cxx:113
 TriMesh.cxx:114
 TriMesh.cxx:115
 TriMesh.cxx:116
 TriMesh.cxx:117
 TriMesh.cxx:118
 TriMesh.cxx:119
 TriMesh.cxx:120
 TriMesh.cxx:121
 TriMesh.cxx:122
 TriMesh.cxx:123
 TriMesh.cxx:124
 TriMesh.cxx:125
 TriMesh.cxx:126
 TriMesh.cxx:127
 TriMesh.cxx:128
 TriMesh.cxx:129
 TriMesh.cxx:130
 TriMesh.cxx:131
 TriMesh.cxx:132
 TriMesh.cxx:133
 TriMesh.cxx:134
 TriMesh.cxx:135
 TriMesh.cxx:136
 TriMesh.cxx:137
 TriMesh.cxx:138
 TriMesh.cxx:139
 TriMesh.cxx:140
 TriMesh.cxx:141
 TriMesh.cxx:142
 TriMesh.cxx:143
 TriMesh.cxx:144
 TriMesh.cxx:145
 TriMesh.cxx:146
 TriMesh.cxx:147
 TriMesh.cxx:148
 TriMesh.cxx:149
 TriMesh.cxx:150
 TriMesh.cxx:151
 TriMesh.cxx:152
 TriMesh.cxx:153
 TriMesh.cxx:154
 TriMesh.cxx:155
 TriMesh.cxx:156
 TriMesh.cxx:157
 TriMesh.cxx:158
 TriMesh.cxx:159
 TriMesh.cxx:160
 TriMesh.cxx:161
 TriMesh.cxx:162
 TriMesh.cxx:163
 TriMesh.cxx:164
 TriMesh.cxx:165
 TriMesh.cxx:166
 TriMesh.cxx:167
 TriMesh.cxx:168
 TriMesh.cxx:169
 TriMesh.cxx:170
 TriMesh.cxx:171
 TriMesh.cxx:172
 TriMesh.cxx:173
 TriMesh.cxx:174
 TriMesh.cxx:175
 TriMesh.cxx:176
 TriMesh.cxx:177
 TriMesh.cxx:178
 TriMesh.cxx:179
 TriMesh.cxx:180
 TriMesh.cxx:181
 TriMesh.cxx:182
 TriMesh.cxx:183
 TriMesh.cxx:184
 TriMesh.cxx:185
 TriMesh.cxx:186
 TriMesh.cxx:187
 TriMesh.cxx:188
 TriMesh.cxx:189
 TriMesh.cxx:190
 TriMesh.cxx:191
 TriMesh.cxx:192
 TriMesh.cxx:193
 TriMesh.cxx:194
 TriMesh.cxx:195
 TriMesh.cxx:196
 TriMesh.cxx:197
 TriMesh.cxx:198
 TriMesh.cxx:199
 TriMesh.cxx:200
 TriMesh.cxx:201
 TriMesh.cxx:202
 TriMesh.cxx:203
 TriMesh.cxx:204
 TriMesh.cxx:205
 TriMesh.cxx:206
 TriMesh.cxx:207
 TriMesh.cxx:208
 TriMesh.cxx:209
 TriMesh.cxx:210
 TriMesh.cxx:211
 TriMesh.cxx:212
 TriMesh.cxx:213
 TriMesh.cxx:214
 TriMesh.cxx:215
 TriMesh.cxx:216
 TriMesh.cxx:217
 TriMesh.cxx:218
 TriMesh.cxx:219
 TriMesh.cxx:220
 TriMesh.cxx:221
 TriMesh.cxx:222
 TriMesh.cxx:223
 TriMesh.cxx:224
 TriMesh.cxx:225
 TriMesh.cxx:226
 TriMesh.cxx:227
 TriMesh.cxx:228
 TriMesh.cxx:229
 TriMesh.cxx:230
 TriMesh.cxx:231
 TriMesh.cxx:232
 TriMesh.cxx:233
 TriMesh.cxx:234
 TriMesh.cxx:235
 TriMesh.cxx:236
 TriMesh.cxx:237
 TriMesh.cxx:238
 TriMesh.cxx:239
 TriMesh.cxx:240
 TriMesh.cxx:241
 TriMesh.cxx:242
 TriMesh.cxx:243
 TriMesh.cxx:244
 TriMesh.cxx:245
 TriMesh.cxx:246
 TriMesh.cxx:247
 TriMesh.cxx:248
 TriMesh.cxx:249
 TriMesh.cxx:250
 TriMesh.cxx:251
 TriMesh.cxx:252
 TriMesh.cxx:253
 TriMesh.cxx:254
 TriMesh.cxx:255
 TriMesh.cxx:256
 TriMesh.cxx:257
 TriMesh.cxx:258
 TriMesh.cxx:259
 TriMesh.cxx:260
 TriMesh.cxx:261
 TriMesh.cxx:262
 TriMesh.cxx:263
 TriMesh.cxx:264
 TriMesh.cxx:265
 TriMesh.cxx:266
 TriMesh.cxx:267
 TriMesh.cxx:268
 TriMesh.cxx:269
 TriMesh.cxx:270
 TriMesh.cxx:271
 TriMesh.cxx:272
 TriMesh.cxx:273
 TriMesh.cxx:274
 TriMesh.cxx:275
 TriMesh.cxx:276
 TriMesh.cxx:277
 TriMesh.cxx:278
 TriMesh.cxx:279
 TriMesh.cxx:280
 TriMesh.cxx:281
 TriMesh.cxx:282
 TriMesh.cxx:283
 TriMesh.cxx:284
 TriMesh.cxx:285
 TriMesh.cxx:286
 TriMesh.cxx:287
 TriMesh.cxx:288
 TriMesh.cxx:289
 TriMesh.cxx:290
 TriMesh.cxx:291
 TriMesh.cxx:292
 TriMesh.cxx:293
 TriMesh.cxx:294
 TriMesh.cxx:295
 TriMesh.cxx:296
 TriMesh.cxx:297
 TriMesh.cxx:298
 TriMesh.cxx:299
 TriMesh.cxx:300
 TriMesh.cxx:301
 TriMesh.cxx:302
 TriMesh.cxx:303
 TriMesh.cxx:304
 TriMesh.cxx:305
 TriMesh.cxx:306
 TriMesh.cxx:307
 TriMesh.cxx:308
 TriMesh.cxx:309
 TriMesh.cxx:310
 TriMesh.cxx:311
 TriMesh.cxx:312
 TriMesh.cxx:313
 TriMesh.cxx:314
 TriMesh.cxx:315
 TriMesh.cxx:316
 TriMesh.cxx:317
 TriMesh.cxx:318
 TriMesh.cxx:319
 TriMesh.cxx:320
 TriMesh.cxx:321
 TriMesh.cxx:322
 TriMesh.cxx:323
 TriMesh.cxx:324
 TriMesh.cxx:325
 TriMesh.cxx:326
 TriMesh.cxx:327
 TriMesh.cxx:328
 TriMesh.cxx:329
 TriMesh.cxx:330
 TriMesh.cxx:331
 TriMesh.cxx:332
 TriMesh.cxx:333
 TriMesh.cxx:334
 TriMesh.cxx:335
 TriMesh.cxx:336
 TriMesh.cxx:337
 TriMesh.cxx:338
 TriMesh.cxx:339
 TriMesh.cxx:340
 TriMesh.cxx:341
 TriMesh.cxx:342
 TriMesh.cxx:343
 TriMesh.cxx:344
 TriMesh.cxx:345
 TriMesh.cxx:346
 TriMesh.cxx:347
 TriMesh.cxx:348
 TriMesh.cxx:349
 TriMesh.cxx:350
 TriMesh.cxx:351
 TriMesh.cxx:352
 TriMesh.cxx:353
 TriMesh.cxx:354
 TriMesh.cxx:355
 TriMesh.cxx:356
 TriMesh.cxx:357
 TriMesh.cxx:358
 TriMesh.cxx:359
 TriMesh.cxx:360
 TriMesh.cxx:361
 TriMesh.cxx:362
 TriMesh.cxx:363
 TriMesh.cxx:364
 TriMesh.cxx:365
 TriMesh.cxx:366
 TriMesh.cxx:367
 TriMesh.cxx:368
 TriMesh.cxx:369
 TriMesh.cxx:370
 TriMesh.cxx:371
 TriMesh.cxx:372
 TriMesh.cxx:373
 TriMesh.cxx:374
 TriMesh.cxx:375
 TriMesh.cxx:376
 TriMesh.cxx:377
 TriMesh.cxx:378
 TriMesh.cxx:379
 TriMesh.cxx:380
 TriMesh.cxx:381
 TriMesh.cxx:382
 TriMesh.cxx:383
 TriMesh.cxx:384
 TriMesh.cxx:385
 TriMesh.cxx:386
 TriMesh.cxx:387
 TriMesh.cxx:388
 TriMesh.cxx:389
 TriMesh.cxx:390
 TriMesh.cxx:391
 TriMesh.cxx:392
 TriMesh.cxx:393
 TriMesh.cxx:394
 TriMesh.cxx:395
 TriMesh.cxx:396
 TriMesh.cxx:397
 TriMesh.cxx:398
 TriMesh.cxx:399
 TriMesh.cxx:400
 TriMesh.cxx:401
 TriMesh.cxx:402
 TriMesh.cxx:403
 TriMesh.cxx:404
 TriMesh.cxx:405
 TriMesh.cxx:406
 TriMesh.cxx:407
 TriMesh.cxx:408
 TriMesh.cxx:409
 TriMesh.cxx:410
 TriMesh.cxx:411
 TriMesh.cxx:412
 TriMesh.cxx:413
 TriMesh.cxx:414
 TriMesh.cxx:415
 TriMesh.cxx:416
 TriMesh.cxx:417
 TriMesh.cxx:418
 TriMesh.cxx:419
 TriMesh.cxx:420
 TriMesh.cxx:421
 TriMesh.cxx:422
 TriMesh.cxx:423
 TriMesh.cxx:424
 TriMesh.cxx:425
 TriMesh.cxx:426
 TriMesh.cxx:427
 TriMesh.cxx:428
 TriMesh.cxx:429
 TriMesh.cxx:430
 TriMesh.cxx:431
 TriMesh.cxx:432
 TriMesh.cxx:433
 TriMesh.cxx:434
 TriMesh.cxx:435
 TriMesh.cxx:436
 TriMesh.cxx:437
 TriMesh.cxx:438
 TriMesh.cxx:439
 TriMesh.cxx:440
 TriMesh.cxx:441
 TriMesh.cxx:442
 TriMesh.cxx:443
 TriMesh.cxx:444
 TriMesh.cxx:445
 TriMesh.cxx:446
 TriMesh.cxx:447
 TriMesh.cxx:448
 TriMesh.cxx:449
 TriMesh.cxx:450
 TriMesh.cxx:451
 TriMesh.cxx:452
 TriMesh.cxx:453
 TriMesh.cxx:454
 TriMesh.cxx:455
 TriMesh.cxx:456
 TriMesh.cxx:457
 TriMesh.cxx:458
 TriMesh.cxx:459
 TriMesh.cxx:460
 TriMesh.cxx:461
 TriMesh.cxx:462
 TriMesh.cxx:463
 TriMesh.cxx:464
 TriMesh.cxx:465
 TriMesh.cxx:466
 TriMesh.cxx:467
 TriMesh.cxx:468
 TriMesh.cxx:469
 TriMesh.cxx:470
 TriMesh.cxx:471
 TriMesh.cxx:472
 TriMesh.cxx:473
 TriMesh.cxx:474
 TriMesh.cxx:475
 TriMesh.cxx:476
 TriMesh.cxx:477
 TriMesh.cxx:478
 TriMesh.cxx:479
 TriMesh.cxx:480
 TriMesh.cxx:481
 TriMesh.cxx:482
 TriMesh.cxx:483
 TriMesh.cxx:484
 TriMesh.cxx:485
 TriMesh.cxx:486
 TriMesh.cxx:487
 TriMesh.cxx:488
 TriMesh.cxx:489
 TriMesh.cxx:490
 TriMesh.cxx:491
 TriMesh.cxx:492
 TriMesh.cxx:493
 TriMesh.cxx:494
 TriMesh.cxx:495
 TriMesh.cxx:496
 TriMesh.cxx:497
 TriMesh.cxx:498
 TriMesh.cxx:499
 TriMesh.cxx:500
 TriMesh.cxx:501
 TriMesh.cxx:502
 TriMesh.cxx:503
 TriMesh.cxx:504
 TriMesh.cxx:505
 TriMesh.cxx:506
 TriMesh.cxx:507
 TriMesh.cxx:508
 TriMesh.cxx:509
 TriMesh.cxx:510
 TriMesh.cxx:511
 TriMesh.cxx:512
 TriMesh.cxx:513
 TriMesh.cxx:514
 TriMesh.cxx:515
 TriMesh.cxx:516
 TriMesh.cxx:517
 TriMesh.cxx:518
 TriMesh.cxx:519
 TriMesh.cxx:520
 TriMesh.cxx:521
 TriMesh.cxx:522
 TriMesh.cxx:523
 TriMesh.cxx:524
 TriMesh.cxx:525
 TriMesh.cxx:526
 TriMesh.cxx:527
 TriMesh.cxx:528
 TriMesh.cxx:529
 TriMesh.cxx:530
 TriMesh.cxx:531
 TriMesh.cxx:532
 TriMesh.cxx:533
 TriMesh.cxx:534
 TriMesh.cxx:535
 TriMesh.cxx:536
 TriMesh.cxx:537
 TriMesh.cxx:538
 TriMesh.cxx:539
 TriMesh.cxx:540
 TriMesh.cxx:541
 TriMesh.cxx:542
 TriMesh.cxx:543
 TriMesh.cxx:544
 TriMesh.cxx:545
 TriMesh.cxx:546
 TriMesh.cxx:547
 TriMesh.cxx:548
 TriMesh.cxx:549
 TriMesh.cxx:550
 TriMesh.cxx:551
 TriMesh.cxx:552
 TriMesh.cxx:553
 TriMesh.cxx:554
 TriMesh.cxx:555
 TriMesh.cxx:556
 TriMesh.cxx:557
 TriMesh.cxx:558
 TriMesh.cxx:559
 TriMesh.cxx:560
 TriMesh.cxx:561
 TriMesh.cxx:562
 TriMesh.cxx:563
 TriMesh.cxx:564
 TriMesh.cxx:565
 TriMesh.cxx:566
 TriMesh.cxx:567
 TriMesh.cxx:568
 TriMesh.cxx:569
 TriMesh.cxx:570
 TriMesh.cxx:571
 TriMesh.cxx:572
 TriMesh.cxx:573
 TriMesh.cxx:574
 TriMesh.cxx:575
 TriMesh.cxx:576
 TriMesh.cxx:577
 TriMesh.cxx:578
 TriMesh.cxx:579
 TriMesh.cxx:580
 TriMesh.cxx:581
 TriMesh.cxx:582
 TriMesh.cxx:583
 TriMesh.cxx:584
 TriMesh.cxx:585
 TriMesh.cxx:586
 TriMesh.cxx:587
 TriMesh.cxx:588
 TriMesh.cxx:589
 TriMesh.cxx:590
 TriMesh.cxx:591
 TriMesh.cxx:592
 TriMesh.cxx:593
 TriMesh.cxx:594
 TriMesh.cxx:595
 TriMesh.cxx:596
 TriMesh.cxx:597
 TriMesh.cxx:598
 TriMesh.cxx:599
 TriMesh.cxx:600
 TriMesh.cxx:601
 TriMesh.cxx:602
 TriMesh.cxx:603
 TriMesh.cxx:604
 TriMesh.cxx:605
 TriMesh.cxx:606
 TriMesh.cxx:607
 TriMesh.cxx:608
 TriMesh.cxx:609
 TriMesh.cxx:610
 TriMesh.cxx:611
 TriMesh.cxx:612
 TriMesh.cxx:613
 TriMesh.cxx:614
 TriMesh.cxx:615
 TriMesh.cxx:616
 TriMesh.cxx:617
 TriMesh.cxx:618
 TriMesh.cxx:619
 TriMesh.cxx:620
 TriMesh.cxx:621
 TriMesh.cxx:622
 TriMesh.cxx:623
 TriMesh.cxx:624
 TriMesh.cxx:625
 TriMesh.cxx:626
 TriMesh.cxx:627
 TriMesh.cxx:628
 TriMesh.cxx:629
 TriMesh.cxx:630
 TriMesh.cxx:631
 TriMesh.cxx:632
 TriMesh.cxx:633
 TriMesh.cxx:634
 TriMesh.cxx:635
 TriMesh.cxx:636
 TriMesh.cxx:637
 TriMesh.cxx:638
 TriMesh.cxx:639
 TriMesh.cxx:640
 TriMesh.cxx:641
 TriMesh.cxx:642
 TriMesh.cxx:643
 TriMesh.cxx:644
 TriMesh.cxx:645
 TriMesh.cxx:646
 TriMesh.cxx:647
 TriMesh.cxx:648
 TriMesh.cxx:649
 TriMesh.cxx:650
 TriMesh.cxx:651
 TriMesh.cxx:652
 TriMesh.cxx:653
 TriMesh.cxx:654
 TriMesh.cxx:655
 TriMesh.cxx:656
 TriMesh.cxx:657
 TriMesh.cxx:658
 TriMesh.cxx:659
 TriMesh.cxx:660
 TriMesh.cxx:661
 TriMesh.cxx:662
 TriMesh.cxx:663
 TriMesh.cxx:664
 TriMesh.cxx:665
 TriMesh.cxx:666
 TriMesh.cxx:667
 TriMesh.cxx:668
 TriMesh.cxx:669
 TriMesh.cxx:670
 TriMesh.cxx:671
 TriMesh.cxx:672
 TriMesh.cxx:673
 TriMesh.cxx:674
 TriMesh.cxx:675
 TriMesh.cxx:676
 TriMesh.cxx:677
 TriMesh.cxx:678
 TriMesh.cxx:679
 TriMesh.cxx:680
 TriMesh.cxx:681
 TriMesh.cxx:682
 TriMesh.cxx:683
 TriMesh.cxx:684
 TriMesh.cxx:685
 TriMesh.cxx:686
 TriMesh.cxx:687
 TriMesh.cxx:688
 TriMesh.cxx:689
 TriMesh.cxx:690
 TriMesh.cxx:691
 TriMesh.cxx:692
 TriMesh.cxx:693
 TriMesh.cxx:694
 TriMesh.cxx:695
 TriMesh.cxx:696
 TriMesh.cxx:697
 TriMesh.cxx:698
 TriMesh.cxx:699
 TriMesh.cxx:700
 TriMesh.cxx:701
 TriMesh.cxx:702
 TriMesh.cxx:703
 TriMesh.cxx:704
 TriMesh.cxx:705
 TriMesh.cxx:706
 TriMesh.cxx:707
 TriMesh.cxx:708
 TriMesh.cxx:709
 TriMesh.cxx:710
 TriMesh.cxx:711
 TriMesh.cxx:712
 TriMesh.cxx:713
 TriMesh.cxx:714
 TriMesh.cxx:715
 TriMesh.cxx:716
 TriMesh.cxx:717
 TriMesh.cxx:718
 TriMesh.cxx:719
 TriMesh.cxx:720
 TriMesh.cxx:721
 TriMesh.cxx:722
 TriMesh.cxx:723
 TriMesh.cxx:724
 TriMesh.cxx:725
 TriMesh.cxx:726
 TriMesh.cxx:727
 TriMesh.cxx:728
 TriMesh.cxx:729
 TriMesh.cxx:730
 TriMesh.cxx:731
 TriMesh.cxx:732
 TriMesh.cxx:733
 TriMesh.cxx:734
 TriMesh.cxx:735
 TriMesh.cxx:736
 TriMesh.cxx:737
 TriMesh.cxx:738
 TriMesh.cxx:739
 TriMesh.cxx:740
 TriMesh.cxx:741
 TriMesh.cxx:742
 TriMesh.cxx:743
 TriMesh.cxx:744
 TriMesh.cxx:745
 TriMesh.cxx:746
 TriMesh.cxx:747
 TriMesh.cxx:748
 TriMesh.cxx:749
 TriMesh.cxx:750
 TriMesh.cxx:751
 TriMesh.cxx:752
 TriMesh.cxx:753
 TriMesh.cxx:754
 TriMesh.cxx:755
 TriMesh.cxx:756
 TriMesh.cxx:757
 TriMesh.cxx:758
 TriMesh.cxx:759
 TriMesh.cxx:760
 TriMesh.cxx:761
 TriMesh.cxx:762
 TriMesh.cxx:763
 TriMesh.cxx:764
 TriMesh.cxx:765
 TriMesh.cxx:766
 TriMesh.cxx:767
 TriMesh.cxx:768
 TriMesh.cxx:769
 TriMesh.cxx:770
 TriMesh.cxx:771
 TriMesh.cxx:772
 TriMesh.cxx:773
 TriMesh.cxx:774
 TriMesh.cxx:775
 TriMesh.cxx:776
 TriMesh.cxx:777
 TriMesh.cxx:778
 TriMesh.cxx:779
 TriMesh.cxx:780
 TriMesh.cxx:781
 TriMesh.cxx:782
 TriMesh.cxx:783
 TriMesh.cxx:784
 TriMesh.cxx:785
 TriMesh.cxx:786
 TriMesh.cxx:787
 TriMesh.cxx:788
 TriMesh.cxx:789
 TriMesh.cxx:790
 TriMesh.cxx:791
 TriMesh.cxx:792
 TriMesh.cxx:793
 TriMesh.cxx:794
 TriMesh.cxx:795
 TriMesh.cxx:796
 TriMesh.cxx:797
 TriMesh.cxx:798
 TriMesh.cxx:799
 TriMesh.cxx:800
 TriMesh.cxx:801
 TriMesh.cxx:802
 TriMesh.cxx:803
 TriMesh.cxx:804
 TriMesh.cxx:805
 TriMesh.cxx:806
 TriMesh.cxx:807
 TriMesh.cxx:808
 TriMesh.cxx:809
 TriMesh.cxx:810
 TriMesh.cxx:811
 TriMesh.cxx:812
 TriMesh.cxx:813
 TriMesh.cxx:814
 TriMesh.cxx:815
 TriMesh.cxx:816
 TriMesh.cxx:817
 TriMesh.cxx:818
 TriMesh.cxx:819
 TriMesh.cxx:820
 TriMesh.cxx:821
 TriMesh.cxx:822
 TriMesh.cxx:823
 TriMesh.cxx:824
 TriMesh.cxx:825
 TriMesh.cxx:826
 TriMesh.cxx:827
 TriMesh.cxx:828
 TriMesh.cxx:829
 TriMesh.cxx:830
 TriMesh.cxx:831
 TriMesh.cxx:832
 TriMesh.cxx:833
 TriMesh.cxx:834
 TriMesh.cxx:835
 TriMesh.cxx:836
 TriMesh.cxx:837
 TriMesh.cxx:838
 TriMesh.cxx:839
 TriMesh.cxx:840
 TriMesh.cxx:841
 TriMesh.cxx:842
 TriMesh.cxx:843
 TriMesh.cxx:844
 TriMesh.cxx:845
 TriMesh.cxx:846
 TriMesh.cxx:847
 TriMesh.cxx:848
 TriMesh.cxx:849
 TriMesh.cxx:850
 TriMesh.cxx:851
 TriMesh.cxx:852
 TriMesh.cxx:853
 TriMesh.cxx:854
 TriMesh.cxx:855
 TriMesh.cxx:856
 TriMesh.cxx:857
 TriMesh.cxx:858
 TriMesh.cxx:859
 TriMesh.cxx:860
 TriMesh.cxx:861
 TriMesh.cxx:862
 TriMesh.cxx:863
 TriMesh.cxx:864
 TriMesh.cxx:865
 TriMesh.cxx:866
 TriMesh.cxx:867
 TriMesh.cxx:868
 TriMesh.cxx:869
 TriMesh.cxx:870
 TriMesh.cxx:871
 TriMesh.cxx:872
 TriMesh.cxx:873
 TriMesh.cxx:874
 TriMesh.cxx:875
 TriMesh.cxx:876
 TriMesh.cxx:877
 TriMesh.cxx:878
 TriMesh.cxx:879
 TriMesh.cxx:880
 TriMesh.cxx:881
 TriMesh.cxx:882
 TriMesh.cxx:883
 TriMesh.cxx:884
 TriMesh.cxx:885
 TriMesh.cxx:886
 TriMesh.cxx:887
 TriMesh.cxx:888
 TriMesh.cxx:889
 TriMesh.cxx:890
 TriMesh.cxx:891
 TriMesh.cxx:892
 TriMesh.cxx:893
 TriMesh.cxx:894
 TriMesh.cxx:895
 TriMesh.cxx:896
 TriMesh.cxx:897
 TriMesh.cxx:898
 TriMesh.cxx:899
 TriMesh.cxx:900
 TriMesh.cxx:901
 TriMesh.cxx:902
 TriMesh.cxx:903
 TriMesh.cxx:904
 TriMesh.cxx:905
 TriMesh.cxx:906
 TriMesh.cxx:907
 TriMesh.cxx:908
 TriMesh.cxx:909
 TriMesh.cxx:910
 TriMesh.cxx:911
 TriMesh.cxx:912
 TriMesh.cxx:913
 TriMesh.cxx:914
 TriMesh.cxx:915
 TriMesh.cxx:916
 TriMesh.cxx:917
 TriMesh.cxx:918
 TriMesh.cxx:919
 TriMesh.cxx:920
 TriMesh.cxx:921
 TriMesh.cxx:922
 TriMesh.cxx:923
 TriMesh.cxx:924
 TriMesh.cxx:925
 TriMesh.cxx:926
 TriMesh.cxx:927
 TriMesh.cxx:928
 TriMesh.cxx:929
 TriMesh.cxx:930
 TriMesh.cxx:931
 TriMesh.cxx:932
 TriMesh.cxx:933
 TriMesh.cxx:934
 TriMesh.cxx:935
 TriMesh.cxx:936
 TriMesh.cxx:937
 TriMesh.cxx:938
 TriMesh.cxx:939
 TriMesh.cxx:940
 TriMesh.cxx:941
 TriMesh.cxx:942
 TriMesh.cxx:943
 TriMesh.cxx:944
 TriMesh.cxx:945
 TriMesh.cxx:946
 TriMesh.cxx:947
 TriMesh.cxx:948
 TriMesh.cxx:949
 TriMesh.cxx:950
 TriMesh.cxx:951
 TriMesh.cxx:952
 TriMesh.cxx:953
 TriMesh.cxx:954
 TriMesh.cxx:955
 TriMesh.cxx:956
 TriMesh.cxx:957
 TriMesh.cxx:958
 TriMesh.cxx:959
 TriMesh.cxx:960
 TriMesh.cxx:961
 TriMesh.cxx:962
 TriMesh.cxx:963
 TriMesh.cxx:964
 TriMesh.cxx:965
 TriMesh.cxx:966
 TriMesh.cxx:967
 TriMesh.cxx:968
 TriMesh.cxx:969
 TriMesh.cxx:970
 TriMesh.cxx:971
 TriMesh.cxx:972
 TriMesh.cxx:973
 TriMesh.cxx:974
 TriMesh.cxx:975
 TriMesh.cxx:976
 TriMesh.cxx:977
 TriMesh.cxx:978
 TriMesh.cxx:979
 TriMesh.cxx:980
 TriMesh.cxx:981
 TriMesh.cxx:982
 TriMesh.cxx:983
 TriMesh.cxx:984
 TriMesh.cxx:985
 TriMesh.cxx:986
 TriMesh.cxx:987
 TriMesh.cxx:988
 TriMesh.cxx:989
 TriMesh.cxx:990
 TriMesh.cxx:991
 TriMesh.cxx:992
 TriMesh.cxx:993
 TriMesh.cxx:994
 TriMesh.cxx:995
 TriMesh.cxx:996
 TriMesh.cxx:997
 TriMesh.cxx:998
 TriMesh.cxx:999
 TriMesh.cxx:1000
 TriMesh.cxx:1001
 TriMesh.cxx:1002
 TriMesh.cxx:1003
 TriMesh.cxx:1004
 TriMesh.cxx:1005
 TriMesh.cxx:1006
 TriMesh.cxx:1007
 TriMesh.cxx:1008
 TriMesh.cxx:1009
 TriMesh.cxx:1010
 TriMesh.cxx:1011
 TriMesh.cxx:1012
 TriMesh.cxx:1013
 TriMesh.cxx:1014
 TriMesh.cxx:1015
 TriMesh.cxx:1016
 TriMesh.cxx:1017
 TriMesh.cxx:1018
 TriMesh.cxx:1019
 TriMesh.cxx:1020
 TriMesh.cxx:1021
 TriMesh.cxx:1022
 TriMesh.cxx:1023
 TriMesh.cxx:1024
 TriMesh.cxx:1025
 TriMesh.cxx:1026
 TriMesh.cxx:1027
 TriMesh.cxx:1028
 TriMesh.cxx:1029
 TriMesh.cxx:1030
 TriMesh.cxx:1031
 TriMesh.cxx:1032
 TriMesh.cxx:1033
 TriMesh.cxx:1034
 TriMesh.cxx:1035
 TriMesh.cxx:1036
 TriMesh.cxx:1037
 TriMesh.cxx:1038
 TriMesh.cxx:1039
 TriMesh.cxx:1040
 TriMesh.cxx:1041
 TriMesh.cxx:1042
 TriMesh.cxx:1043
 TriMesh.cxx:1044
 TriMesh.cxx:1045
 TriMesh.cxx:1046
 TriMesh.cxx:1047
 TriMesh.cxx:1048
 TriMesh.cxx:1049
 TriMesh.cxx:1050
 TriMesh.cxx:1051
 TriMesh.cxx:1052
 TriMesh.cxx:1053
 TriMesh.cxx:1054
 TriMesh.cxx:1055
 TriMesh.cxx:1056
 TriMesh.cxx:1057
 TriMesh.cxx:1058
 TriMesh.cxx:1059
 TriMesh.cxx:1060
 TriMesh.cxx:1061
 TriMesh.cxx:1062
 TriMesh.cxx:1063
 TriMesh.cxx:1064
 TriMesh.cxx:1065
 TriMesh.cxx:1066
 TriMesh.cxx:1067
 TriMesh.cxx:1068
 TriMesh.cxx:1069
 TriMesh.cxx:1070
 TriMesh.cxx:1071
 TriMesh.cxx:1072
 TriMesh.cxx:1073
 TriMesh.cxx:1074
 TriMesh.cxx:1075
 TriMesh.cxx:1076
 TriMesh.cxx:1077
 TriMesh.cxx:1078
 TriMesh.cxx:1079
 TriMesh.cxx:1080
 TriMesh.cxx:1081
 TriMesh.cxx:1082
 TriMesh.cxx:1083
 TriMesh.cxx:1084
 TriMesh.cxx:1085
 TriMesh.cxx:1086
 TriMesh.cxx:1087
 TriMesh.cxx:1088
 TriMesh.cxx:1089
 TriMesh.cxx:1090
 TriMesh.cxx:1091
 TriMesh.cxx:1092
 TriMesh.cxx:1093
 TriMesh.cxx:1094
 TriMesh.cxx:1095
 TriMesh.cxx:1096
 TriMesh.cxx:1097
 TriMesh.cxx:1098
 TriMesh.cxx:1099
 TriMesh.cxx:1100
 TriMesh.cxx:1101
 TriMesh.cxx:1102
 TriMesh.cxx:1103
 TriMesh.cxx:1104
 TriMesh.cxx:1105
 TriMesh.cxx:1106
 TriMesh.cxx:1107
 TriMesh.cxx:1108
 TriMesh.cxx:1109
 TriMesh.cxx:1110
 TriMesh.cxx:1111
 TriMesh.cxx:1112
 TriMesh.cxx:1113
 TriMesh.cxx:1114
 TriMesh.cxx:1115
 TriMesh.cxx:1116
 TriMesh.cxx:1117
 TriMesh.cxx:1118
 TriMesh.cxx:1119
 TriMesh.cxx:1120
 TriMesh.cxx:1121
 TriMesh.cxx:1122
 TriMesh.cxx:1123
 TriMesh.cxx:1124
 TriMesh.cxx:1125
 TriMesh.cxx:1126
 TriMesh.cxx:1127
 TriMesh.cxx:1128
 TriMesh.cxx:1129
 TriMesh.cxx:1130
 TriMesh.cxx:1131
 TriMesh.cxx:1132
 TriMesh.cxx:1133
 TriMesh.cxx:1134
 TriMesh.cxx:1135
 TriMesh.cxx:1136
 TriMesh.cxx:1137
 TriMesh.cxx:1138
 TriMesh.cxx:1139
 TriMesh.cxx:1140
 TriMesh.cxx:1141
 TriMesh.cxx:1142
 TriMesh.cxx:1143
 TriMesh.cxx:1144
 TriMesh.cxx:1145
 TriMesh.cxx:1146
 TriMesh.cxx:1147
 TriMesh.cxx:1148
 TriMesh.cxx:1149
 TriMesh.cxx:1150
 TriMesh.cxx:1151
 TriMesh.cxx:1152
 TriMesh.cxx:1153
 TriMesh.cxx:1154
 TriMesh.cxx:1155
 TriMesh.cxx:1156
 TriMesh.cxx:1157
 TriMesh.cxx:1158
 TriMesh.cxx:1159
 TriMesh.cxx:1160
 TriMesh.cxx:1161
 TriMesh.cxx:1162
 TriMesh.cxx:1163
 TriMesh.cxx:1164
 TriMesh.cxx:1165
 TriMesh.cxx:1166
 TriMesh.cxx:1167
 TriMesh.cxx:1168
 TriMesh.cxx:1169
 TriMesh.cxx:1170
 TriMesh.cxx:1171
 TriMesh.cxx:1172
 TriMesh.cxx:1173
 TriMesh.cxx:1174
 TriMesh.cxx:1175
 TriMesh.cxx:1176
 TriMesh.cxx:1177
 TriMesh.cxx:1178
 TriMesh.cxx:1179
 TriMesh.cxx:1180
 TriMesh.cxx:1181
 TriMesh.cxx:1182
 TriMesh.cxx:1183
 TriMesh.cxx:1184
 TriMesh.cxx:1185
 TriMesh.cxx:1186
 TriMesh.cxx:1187
 TriMesh.cxx:1188
 TriMesh.cxx:1189
 TriMesh.cxx:1190
 TriMesh.cxx:1191
 TriMesh.cxx:1192
 TriMesh.cxx:1193
 TriMesh.cxx:1194
 TriMesh.cxx:1195
 TriMesh.cxx:1196
 TriMesh.cxx:1197
 TriMesh.cxx:1198
 TriMesh.cxx:1199
 TriMesh.cxx:1200
 TriMesh.cxx:1201
 TriMesh.cxx:1202
 TriMesh.cxx:1203
 TriMesh.cxx:1204
 TriMesh.cxx:1205
 TriMesh.cxx:1206
 TriMesh.cxx:1207
 TriMesh.cxx:1208
 TriMesh.cxx:1209
 TriMesh.cxx:1210
 TriMesh.cxx:1211
 TriMesh.cxx:1212
 TriMesh.cxx:1213
 TriMesh.cxx:1214
 TriMesh.cxx:1215
 TriMesh.cxx:1216
 TriMesh.cxx:1217
 TriMesh.cxx:1218
 TriMesh.cxx:1219
 TriMesh.cxx:1220
 TriMesh.cxx:1221
 TriMesh.cxx:1222
 TriMesh.cxx:1223
 TriMesh.cxx:1224
 TriMesh.cxx:1225
 TriMesh.cxx:1226
 TriMesh.cxx:1227
 TriMesh.cxx:1228
 TriMesh.cxx:1229
 TriMesh.cxx:1230
 TriMesh.cxx:1231
 TriMesh.cxx:1232
 TriMesh.cxx:1233
 TriMesh.cxx:1234
 TriMesh.cxx:1235
 TriMesh.cxx:1236
 TriMesh.cxx:1237
 TriMesh.cxx:1238
 TriMesh.cxx:1239
 TriMesh.cxx:1240
 TriMesh.cxx:1241
 TriMesh.cxx:1242
 TriMesh.cxx:1243
 TriMesh.cxx:1244
 TriMesh.cxx:1245
 TriMesh.cxx:1246
 TriMesh.cxx:1247
 TriMesh.cxx:1248
 TriMesh.cxx:1249
 TriMesh.cxx:1250
 TriMesh.cxx:1251
 TriMesh.cxx:1252
 TriMesh.cxx:1253
 TriMesh.cxx:1254
 TriMesh.cxx:1255
 TriMesh.cxx:1256
 TriMesh.cxx:1257
 TriMesh.cxx:1258
 TriMesh.cxx:1259
 TriMesh.cxx:1260
 TriMesh.cxx:1261
 TriMesh.cxx:1262
 TriMesh.cxx:1263
 TriMesh.cxx:1264
 TriMesh.cxx:1265
 TriMesh.cxx:1266
 TriMesh.cxx:1267
 TriMesh.cxx:1268
 TriMesh.cxx:1269
 TriMesh.cxx:1270
 TriMesh.cxx:1271
 TriMesh.cxx:1272
 TriMesh.cxx:1273
 TriMesh.cxx:1274
 TriMesh.cxx:1275
 TriMesh.cxx:1276
 TriMesh.cxx:1277
 TriMesh.cxx:1278
 TriMesh.cxx:1279
 TriMesh.cxx:1280
 TriMesh.cxx:1281
 TriMesh.cxx:1282
 TriMesh.cxx:1283
 TriMesh.cxx:1284
 TriMesh.cxx:1285
 TriMesh.cxx:1286
 TriMesh.cxx:1287
 TriMesh.cxx:1288
 TriMesh.cxx:1289
 TriMesh.cxx:1290
 TriMesh.cxx:1291
 TriMesh.cxx:1292
 TriMesh.cxx:1293
 TriMesh.cxx:1294
 TriMesh.cxx:1295
 TriMesh.cxx:1296
 TriMesh.cxx:1297
 TriMesh.cxx:1298
 TriMesh.cxx:1299
 TriMesh.cxx:1300
 TriMesh.cxx:1301
 TriMesh.cxx:1302
 TriMesh.cxx:1303
 TriMesh.cxx:1304
 TriMesh.cxx:1305
 TriMesh.cxx:1306
 TriMesh.cxx:1307
 TriMesh.cxx:1308
 TriMesh.cxx:1309
 TriMesh.cxx:1310
 TriMesh.cxx:1311
 TriMesh.cxx:1312
 TriMesh.cxx:1313
 TriMesh.cxx:1314
 TriMesh.cxx:1315
 TriMesh.cxx:1316
 TriMesh.cxx:1317
 TriMesh.cxx:1318
 TriMesh.cxx:1319
 TriMesh.cxx:1320
 TriMesh.cxx:1321
 TriMesh.cxx:1322
 TriMesh.cxx:1323
 TriMesh.cxx:1324
 TriMesh.cxx:1325
 TriMesh.cxx:1326
 TriMesh.cxx:1327
 TriMesh.cxx:1328
 TriMesh.cxx:1329
 TriMesh.cxx:1330
 TriMesh.cxx:1331
 TriMesh.cxx:1332
 TriMesh.cxx:1333
 TriMesh.cxx:1334
 TriMesh.cxx:1335
 TriMesh.cxx:1336
 TriMesh.cxx:1337
 TriMesh.cxx:1338
 TriMesh.cxx:1339
 TriMesh.cxx:1340
 TriMesh.cxx:1341
 TriMesh.cxx:1342
 TriMesh.cxx:1343
 TriMesh.cxx:1344
 TriMesh.cxx:1345
 TriMesh.cxx:1346
 TriMesh.cxx:1347
 TriMesh.cxx:1348
 TriMesh.cxx:1349
 TriMesh.cxx:1350
 TriMesh.cxx:1351
 TriMesh.cxx:1352
 TriMesh.cxx:1353
 TriMesh.cxx:1354
 TriMesh.cxx:1355
 TriMesh.cxx:1356
 TriMesh.cxx:1357
 TriMesh.cxx:1358
 TriMesh.cxx:1359
 TriMesh.cxx:1360
 TriMesh.cxx:1361
 TriMesh.cxx:1362
 TriMesh.cxx:1363
 TriMesh.cxx:1364
 TriMesh.cxx:1365
 TriMesh.cxx:1366
 TriMesh.cxx:1367
 TriMesh.cxx:1368
 TriMesh.cxx:1369
 TriMesh.cxx:1370
 TriMesh.cxx:1371
 TriMesh.cxx:1372
 TriMesh.cxx:1373
 TriMesh.cxx:1374
 TriMesh.cxx:1375
 TriMesh.cxx:1376
 TriMesh.cxx:1377
 TriMesh.cxx:1378
 TriMesh.cxx:1379
 TriMesh.cxx:1380
 TriMesh.cxx:1381
 TriMesh.cxx:1382
 TriMesh.cxx:1383
 TriMesh.cxx:1384
 TriMesh.cxx:1385
 TriMesh.cxx:1386
 TriMesh.cxx:1387
 TriMesh.cxx:1388
 TriMesh.cxx:1389
 TriMesh.cxx:1390
 TriMesh.cxx:1391
 TriMesh.cxx:1392
 TriMesh.cxx:1393
 TriMesh.cxx:1394
 TriMesh.cxx:1395
 TriMesh.cxx:1396
 TriMesh.cxx:1397
 TriMesh.cxx:1398
 TriMesh.cxx:1399
 TriMesh.cxx:1400
 TriMesh.cxx:1401
 TriMesh.cxx:1402
 TriMesh.cxx:1403
 TriMesh.cxx:1404
 TriMesh.cxx:1405
 TriMesh.cxx:1406
 TriMesh.cxx:1407
 TriMesh.cxx:1408
 TriMesh.cxx:1409
 TriMesh.cxx:1410
 TriMesh.cxx:1411
 TriMesh.cxx:1412
 TriMesh.cxx:1413
 TriMesh.cxx:1414
 TriMesh.cxx:1415
 TriMesh.cxx:1416
 TriMesh.cxx:1417
 TriMesh.cxx:1418
 TriMesh.cxx:1419
 TriMesh.cxx:1420
 TriMesh.cxx:1421
 TriMesh.cxx:1422
 TriMesh.cxx:1423
 TriMesh.cxx:1424
 TriMesh.cxx:1425
 TriMesh.cxx:1426
 TriMesh.cxx:1427
 TriMesh.cxx:1428
 TriMesh.cxx:1429
 TriMesh.cxx:1430
 TriMesh.cxx:1431
 TriMesh.cxx:1432
 TriMesh.cxx:1433
 TriMesh.cxx:1434
 TriMesh.cxx:1435
 TriMesh.cxx:1436
 TriMesh.cxx:1437
 TriMesh.cxx:1438
 TriMesh.cxx:1439
 TriMesh.cxx:1440
 TriMesh.cxx:1441
 TriMesh.cxx:1442
 TriMesh.cxx:1443
 TriMesh.cxx:1444
 TriMesh.cxx:1445
 TriMesh.cxx:1446
 TriMesh.cxx:1447
 TriMesh.cxx:1448
 TriMesh.cxx:1449
 TriMesh.cxx:1450
 TriMesh.cxx:1451
 TriMesh.cxx:1452
 TriMesh.cxx:1453
 TriMesh.cxx:1454
 TriMesh.cxx:1455
 TriMesh.cxx:1456
 TriMesh.cxx:1457
 TriMesh.cxx:1458
 TriMesh.cxx:1459
 TriMesh.cxx:1460
 TriMesh.cxx:1461
 TriMesh.cxx:1462
 TriMesh.cxx:1463
 TriMesh.cxx:1464
 TriMesh.cxx:1465
 TriMesh.cxx:1466
 TriMesh.cxx:1467
 TriMesh.cxx:1468
 TriMesh.cxx:1469
 TriMesh.cxx:1470
 TriMesh.cxx:1471
 TriMesh.cxx:1472
 TriMesh.cxx:1473
 TriMesh.cxx:1474
 TriMesh.cxx:1475
 TriMesh.cxx:1476
 TriMesh.cxx:1477
 TriMesh.cxx:1478
 TriMesh.cxx:1479
 TriMesh.cxx:1480
 TriMesh.cxx:1481
 TriMesh.cxx:1482
 TriMesh.cxx:1483
 TriMesh.cxx:1484
 TriMesh.cxx:1485
 TriMesh.cxx:1486
 TriMesh.cxx:1487
 TriMesh.cxx:1488
 TriMesh.cxx:1489
 TriMesh.cxx:1490
 TriMesh.cxx:1491
 TriMesh.cxx:1492
 TriMesh.cxx:1493
 TriMesh.cxx:1494
 TriMesh.cxx:1495
 TriMesh.cxx:1496
 TriMesh.cxx:1497
 TriMesh.cxx:1498
 TriMesh.cxx:1499
 TriMesh.cxx:1500
 TriMesh.cxx:1501
 TriMesh.cxx:1502
 TriMesh.cxx:1503
 TriMesh.cxx:1504
 TriMesh.cxx:1505
 TriMesh.cxx:1506
 TriMesh.cxx:1507
 TriMesh.cxx:1508
 TriMesh.cxx:1509
 TriMesh.cxx:1510
 TriMesh.cxx:1511
 TriMesh.cxx:1512
 TriMesh.cxx:1513
 TriMesh.cxx:1514
 TriMesh.cxx:1515
 TriMesh.cxx:1516
 TriMesh.cxx:1517
 TriMesh.cxx:1518
 TriMesh.cxx:1519
 TriMesh.cxx:1520
 TriMesh.cxx:1521
 TriMesh.cxx:1522
 TriMesh.cxx:1523
 TriMesh.cxx:1524
 TriMesh.cxx:1525
 TriMesh.cxx:1526
 TriMesh.cxx:1527
 TriMesh.cxx:1528
 TriMesh.cxx:1529
 TriMesh.cxx:1530
 TriMesh.cxx:1531
 TriMesh.cxx:1532
 TriMesh.cxx:1533
 TriMesh.cxx:1534
 TriMesh.cxx:1535
 TriMesh.cxx:1536
 TriMesh.cxx:1537
 TriMesh.cxx:1538
 TriMesh.cxx:1539
 TriMesh.cxx:1540
 TriMesh.cxx:1541
 TriMesh.cxx:1542
 TriMesh.cxx:1543
 TriMesh.cxx:1544
 TriMesh.cxx:1545
 TriMesh.cxx:1546
 TriMesh.cxx:1547
 TriMesh.cxx:1548
 TriMesh.cxx:1549
 TriMesh.cxx:1550
 TriMesh.cxx:1551
 TriMesh.cxx:1552
 TriMesh.cxx:1553
 TriMesh.cxx:1554
 TriMesh.cxx:1555
 TriMesh.cxx:1556
 TriMesh.cxx:1557
 TriMesh.cxx:1558
 TriMesh.cxx:1559
 TriMesh.cxx:1560
 TriMesh.cxx:1561
 TriMesh.cxx:1562
 TriMesh.cxx:1563
 TriMesh.cxx:1564
 TriMesh.cxx:1565
 TriMesh.cxx:1566
 TriMesh.cxx:1567
 TriMesh.cxx:1568
 TriMesh.cxx:1569
 TriMesh.cxx:1570
 TriMesh.cxx:1571
 TriMesh.cxx:1572
 TriMesh.cxx:1573
 TriMesh.cxx:1574
 TriMesh.cxx:1575
 TriMesh.cxx:1576
 TriMesh.cxx:1577
 TriMesh.cxx:1578
 TriMesh.cxx:1579
 TriMesh.cxx:1580
 TriMesh.cxx:1581
 TriMesh.cxx:1582
 TriMesh.cxx:1583
 TriMesh.cxx:1584
 TriMesh.cxx:1585
 TriMesh.cxx:1586
 TriMesh.cxx:1587
 TriMesh.cxx:1588
 TriMesh.cxx:1589
 TriMesh.cxx:1590
 TriMesh.cxx:1591
 TriMesh.cxx:1592
 TriMesh.cxx:1593
 TriMesh.cxx:1594
 TriMesh.cxx:1595
 TriMesh.cxx:1596
 TriMesh.cxx:1597
 TriMesh.cxx:1598
 TriMesh.cxx:1599
 TriMesh.cxx:1600
 TriMesh.cxx:1601
 TriMesh.cxx:1602
 TriMesh.cxx:1603
 TriMesh.cxx:1604
 TriMesh.cxx:1605
 TriMesh.cxx:1606
 TriMesh.cxx:1607
 TriMesh.cxx:1608
 TriMesh.cxx:1609
 TriMesh.cxx:1610
 TriMesh.cxx:1611
 TriMesh.cxx:1612
 TriMesh.cxx:1613
 TriMesh.cxx:1614
 TriMesh.cxx:1615
 TriMesh.cxx:1616
 TriMesh.cxx:1617
 TriMesh.cxx:1618
 TriMesh.cxx:1619
 TriMesh.cxx:1620
 TriMesh.cxx:1621
 TriMesh.cxx:1622
 TriMesh.cxx:1623
 TriMesh.cxx:1624
 TriMesh.cxx:1625
 TriMesh.cxx:1626
 TriMesh.cxx:1627
 TriMesh.cxx:1628
 TriMesh.cxx:1629
 TriMesh.cxx:1630
 TriMesh.cxx:1631
 TriMesh.cxx:1632
 TriMesh.cxx:1633
 TriMesh.cxx:1634
 TriMesh.cxx:1635
 TriMesh.cxx:1636
 TriMesh.cxx:1637
 TriMesh.cxx:1638
 TriMesh.cxx:1639
 TriMesh.cxx:1640
 TriMesh.cxx:1641
 TriMesh.cxx:1642
 TriMesh.cxx:1643
 TriMesh.cxx:1644
 TriMesh.cxx:1645
 TriMesh.cxx:1646
 TriMesh.cxx:1647
 TriMesh.cxx:1648
 TriMesh.cxx:1649
 TriMesh.cxx:1650
 TriMesh.cxx:1651
 TriMesh.cxx:1652
 TriMesh.cxx:1653
 TriMesh.cxx:1654
 TriMesh.cxx:1655
 TriMesh.cxx:1656
 TriMesh.cxx:1657
 TriMesh.cxx:1658
 TriMesh.cxx:1659
 TriMesh.cxx:1660
 TriMesh.cxx:1661
 TriMesh.cxx:1662
 TriMesh.cxx:1663
 TriMesh.cxx:1664
 TriMesh.cxx:1665
 TriMesh.cxx:1666
 TriMesh.cxx:1667
 TriMesh.cxx:1668
 TriMesh.cxx:1669
 TriMesh.cxx:1670
 TriMesh.cxx:1671
 TriMesh.cxx:1672
 TriMesh.cxx:1673
 TriMesh.cxx:1674
 TriMesh.cxx:1675
 TriMesh.cxx:1676
 TriMesh.cxx:1677
 TriMesh.cxx:1678
 TriMesh.cxx:1679
 TriMesh.cxx:1680
 TriMesh.cxx:1681
 TriMesh.cxx:1682
 TriMesh.cxx:1683
 TriMesh.cxx:1684
 TriMesh.cxx:1685
 TriMesh.cxx:1686
 TriMesh.cxx:1687
 TriMesh.cxx:1688
 TriMesh.cxx:1689
 TriMesh.cxx:1690
 TriMesh.cxx:1691
 TriMesh.cxx:1692
 TriMesh.cxx:1693
 TriMesh.cxx:1694
 TriMesh.cxx:1695
 TriMesh.cxx:1696
 TriMesh.cxx:1697
 TriMesh.cxx:1698
 TriMesh.cxx:1699
 TriMesh.cxx:1700
 TriMesh.cxx:1701
 TriMesh.cxx:1702
 TriMesh.cxx:1703
 TriMesh.cxx:1704
 TriMesh.cxx:1705
 TriMesh.cxx:1706
 TriMesh.cxx:1707
 TriMesh.cxx:1708
 TriMesh.cxx:1709
 TriMesh.cxx:1710
 TriMesh.cxx:1711
 TriMesh.cxx:1712
 TriMesh.cxx:1713
 TriMesh.cxx:1714
 TriMesh.cxx:1715
 TriMesh.cxx:1716
 TriMesh.cxx:1717
 TriMesh.cxx:1718
 TriMesh.cxx:1719
 TriMesh.cxx:1720
 TriMesh.cxx:1721
 TriMesh.cxx:1722
 TriMesh.cxx:1723
 TriMesh.cxx:1724
 TriMesh.cxx:1725
 TriMesh.cxx:1726
 TriMesh.cxx:1727
 TriMesh.cxx:1728
 TriMesh.cxx:1729
 TriMesh.cxx:1730
 TriMesh.cxx:1731
 TriMesh.cxx:1732
 TriMesh.cxx:1733
 TriMesh.cxx:1734
 TriMesh.cxx:1735
 TriMesh.cxx:1736
 TriMesh.cxx:1737
 TriMesh.cxx:1738
 TriMesh.cxx:1739
 TriMesh.cxx:1740
 TriMesh.cxx:1741
 TriMesh.cxx:1742
 TriMesh.cxx:1743
 TriMesh.cxx:1744
 TriMesh.cxx:1745
 TriMesh.cxx:1746
 TriMesh.cxx:1747
 TriMesh.cxx:1748
 TriMesh.cxx:1749
 TriMesh.cxx:1750
 TriMesh.cxx:1751
 TriMesh.cxx:1752
 TriMesh.cxx:1753
 TriMesh.cxx:1754
 TriMesh.cxx:1755
 TriMesh.cxx:1756
 TriMesh.cxx:1757
 TriMesh.cxx:1758
 TriMesh.cxx:1759
 TriMesh.cxx:1760
 TriMesh.cxx:1761
 TriMesh.cxx:1762
 TriMesh.cxx:1763
 TriMesh.cxx:1764
 TriMesh.cxx:1765
 TriMesh.cxx:1766
 TriMesh.cxx:1767
 TriMesh.cxx:1768
 TriMesh.cxx:1769
 TriMesh.cxx:1770
 TriMesh.cxx:1771
 TriMesh.cxx:1772
 TriMesh.cxx:1773
 TriMesh.cxx:1774
 TriMesh.cxx:1775
 TriMesh.cxx:1776
 TriMesh.cxx:1777
 TriMesh.cxx:1778
 TriMesh.cxx:1779
 TriMesh.cxx:1780
 TriMesh.cxx:1781