netgen/libsrc/meshing/adfront3.cpp
Joachim Schoeberl 8f73a00d2d code cleanup
2024-12-15 18:00:50 +01:00

950 lines
21 KiB
C++

#include <mystdlib.h>
#include <gprim/geomtest3d.hpp>
#include "adfront3.hpp"
/* ********************** FrontPoint ********************** */
namespace netgen
{
FrontPoint3 :: FrontPoint3 ()
{
globalindex.Invalidate(); // = -1;
nfacetopoint = 0;
frontnr = 1000;
cluster = PointIndex::INVALID;
}
FrontPoint3 :: FrontPoint3 (const Point<3> & ap, PointIndex agi)
{
p = ap;
globalindex = agi;
nfacetopoint = 0;
frontnr = 1000;
cluster = PointIndex::INVALID;
}
/* ********************** FrontFace ********************** */
FrontFace :: FrontFace ()
{
qualclass = 1;
oldfront = 0;
hashvalue = 0;
cluster = PointIndex::INVALID;
}
FrontFace :: FrontFace (const MiniElement2d & af)
{
f = af;
oldfront = 0;
qualclass = 1;
hashvalue = 0;
}
void FrontFace :: Invalidate ()
{
f.Delete();
oldfront = 0;
qualclass = 1000;
}
/* ********************** AddFront ********************** */
AdFront3 :: AdFront3 ()
{
nff = 0;
nff4 = 0;
vol = 0;
hashon = 1;
hashcreated = 0;
if (hashon)
hashtable.Init(&points, &faces);
facetree = NULL;
// connectedpairs = NULL;
rebuildcounter = -1;
lasti = 0;
minval = -1;
}
AdFront3 :: ~AdFront3 ()
{
delete facetree;
// delete connectedpairs;
}
void AdFront3 :: GetPoints (NgArray<Point<3> > & apoints) const
{
/*
for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
apoints.Append (points[pi].P());
*/
for (auto & p : points)
apoints.Append(p.P());
}
PointIndex AdFront3 :: AddPoint (const Point<3> & p, PointIndex globind)
{
if (delpointl.Size())
{
PointIndex pi = delpointl.Last();
delpointl.DeleteLast ();
points[pi] = FrontPoint3 (p, globind);
return pi;
}
else
{
points.Append (FrontPoint3 (p, globind));
// return --points.End();
return *points.Range().end()-1;
// return points.Size()-1+PointIndex::BASE;
}
}
INDEX AdFront3 :: AddFace (const MiniElement2d & aface)
{
int i, minfn;
nff++;
for (i = 0; i < aface.GetNP(); i++)
points[aface[i]].AddFace();
const Point3d & p1 = points[aface[0]].P();
const Point3d & p2 = points[aface[1]].P();
const Point3d & p3 = points[aface[2]].P();
vol += 1.0/6.0 * (p1.X() + p2.X() + p3.X()) *
( (p2.Y() - p1.Y()) * (p3.Z() - p1.Z()) -
(p2.Z() - p1.Z()) * (p3.Y() - p1.Y()) );
if (aface.GetNP() == 4)
{
nff4++;
const Point3d & p4 = points[aface[3]].P();
vol += 1.0/6.0 * (p1.X() + p3.X() + p4.X()) *
( (p3.Y() - p1.Y()) * (p4.Z() - p1.Z()) -
(p3.Z() - p1.Z()) * (p4.Y() - p1.Y()) );
}
minfn = 1000;
for (i = 0; i < aface.GetNP(); i++)
{
int fpn = points[aface[i]].FrontNr();
if (i == 0 || fpn < minfn)
minfn = fpn;
}
PointIndex cluster = PointIndex::INVALID;
for (i = 1; i <= aface.GetNP(); i++)
{
if (points[aface.PNum(i)].cluster.IsValid())
cluster = points[aface.PNum(i)].cluster;
}
for (i = 1; i <= aface.GetNP(); i++)
points[aface.PNum(i)].cluster = cluster;
for (i = 1; i <= aface.GetNP(); i++)
points[aface.PNum(i)].DecFrontNr (minfn+1);
faces.Append(FrontFace (aface));
int nfn = faces.Size();
faces.Elem(nfn).cluster = cluster;
if (hashon && hashcreated)
hashtable.AddElem(aface, nfn);
return nfn;
}
void AdFront3 :: DeleteFace (INDEX fi)
{
nff--;
/*
for (int i = 1; i <= faces.Get(fi).Face().GetNP(); i++)
{
PointIndex pi = faces.Get(fi).Face().PNum(i);
*/
for (PointIndex pi : faces.Get(fi).Face().PNums())
{
points[pi].RemoveFace();
if (!points[pi].Valid())
delpointl.Append (pi);
}
const MiniElement2d & face = faces.Get(fi).Face();
const Point3d & p1 = points[face.PNum(1)].P();
const Point3d & p2 = points[face.PNum(2)].P();
const Point3d & p3 = points[face.PNum(3)].P();
vol -= 1.0/6.0 * (p1.X() + p2.X() + p3.X()) *
( (p2.Y() - p1.Y()) * (p3.Z() - p1.Z()) -
(p2.Z() - p1.Z()) * (p3.Y() - p1.Y()) );
if (face.GetNP() == 4)
{
const Point3d & p4 = points[face.PNum(4)].P();
vol -= 1.0/6.0 * (p1.X() + p3.X() + p4.X()) *
( (p3.Y() - p1.Y()) * (p4.Z() - p1.Z()) -
(p3.Z() - p1.Z()) * (p4.Y() - p1.Y()) );
nff4--;
}
faces.Elem(fi).Invalidate();
}
INDEX AdFront3 :: AddConnectedPair (PointIndices<2> apair)
{
if (!connectedpairs)
connectedpairs = make_unique<DynamicTable<PointIndex, PointIndex>> (GetNP());
connectedpairs->Add (apair[0], apair[1]);
connectedpairs->Add (apair[1], apair[0]);
return 0;
}
void AdFront3 :: CreateTrees ()
{
int i, j;
PointIndex pi;
Point3d pmin, pmax;
for (pi = IndexBASE<PointIndex>();
pi < GetNP()+IndexBASE<PointIndex>(); pi++)
{
const Point<3> & p = GetPoint(pi);
if (pi == IndexBASE<PointIndex>())
{
pmin = p;
pmax = p;
}
else
{
pmin.SetToMin (p);
pmax.SetToMax (p);
}
}
pmax = pmax + 0.5 * (pmax - pmin);
pmin = pmin + 0.5 * (pmin - pmax);
delete facetree;
facetree = new BoxTree<3> (pmin, pmax);
for (i = 1; i <= GetNF(); i++)
{
const MiniElement2d & el = GetFace(i);
pmin = GetPoint (el[0]);
pmax = pmin;
for (j = 1; j < 3; j++)
{
const Point<3> & p = GetPoint (el[j]);
pmin.SetToMin (p);
pmax.SetToMax (p);
}
pmax = pmax + 0.01 * (pmax - pmin);
pmin = pmin + 0.01 * (pmin - pmax);
// (*testout) << "insert " << i << ": " << pmin << " - " << pmax << "\n";
facetree -> Insert (pmin, pmax, i);
}
}
void AdFront3 :: GetIntersectingFaces (const Point<3> & pmin, const Point<3> & pmax,
NgArray<int> & ifaces) const
{
facetree -> GetIntersecting (pmin, pmax, ifaces);
}
void AdFront3 :: GetFaceBoundingBox (int i, Box3d & box) const
{
const FrontFace & face = faces.Get(i);
box.SetPoint (points[face.f[0]].p);
box.AddPoint (points[face.f[1]].p);
box.AddPoint (points[face.f[2]].p);
}
void AdFront3 :: RebuildInternalTables ()
{
static int timer_a = NgProfiler::CreateTimer ("Adfront3::RebuildInternal A");
static int timer_b = NgProfiler::CreateTimer ("Adfront3::RebuildInternal B");
static int timer_c = NgProfiler::CreateTimer ("Adfront3::RebuildInternal C");
static int timer_d = NgProfiler::CreateTimer ("Adfront3::RebuildInternal D");
NgProfiler::StartTimer (timer_a);
int hi = 0;
for (int i = 1; i <= faces.Size(); i++)
if (faces.Get(i).Valid())
{
hi++;
if (hi < i)
faces.Elem(hi) = faces.Get(i);
}
faces.SetSize (nff);
int np = points.Size();
// for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
for (PointIndex pi : points.Range())
points[pi].cluster = pi;
NgProfiler::StopTimer (timer_a);
NgProfiler::StartTimer (timer_b);
int change;
do
{
change = 0;
for (int i = 1; i <= faces.Size(); i++)
{
const MiniElement2d & el = faces.Get(i).Face();
PointIndex mini = points[el.PNum(1)].cluster;
PointIndex maxi = mini;
for (int j = 2; j <= 3; j++)
{
PointIndex ci = points[el.PNum(j)].cluster;
if (ci < mini) mini = ci;
if (ci > maxi) maxi = ci;
}
if (mini < maxi)
{
change = 1;
for (int j = 1; j <= 3; j++)
points[el.PNum(j)].cluster = mini;
}
}
}
while (change);
NgProfiler::StopTimer (timer_b);
NgProfiler::StartTimer (timer_c);
Array<bool, PointIndex> usecl(np);
usecl = false;
for (int i = 1; i <= faces.Size(); i++)
{
usecl[points[faces.Get(i).Face().PNum(1)].cluster] = true;
faces.Elem(i).cluster =
points[faces.Get(i).Face().PNum(1)].cluster;
}
/*
int cntcl = 0;
for (int i = PointIndex::BASE;
i < np+PointIndex::BASE; i++)
if (usecl[i])
cntcl++;
*/
Array<double, PointIndex> clvol (np);
clvol = 0.0;
for (int i = 1; i <= faces.Size(); i++)
{
const MiniElement2d & face = faces.Get(i).Face();
const Point3d p1 = points[face.PNum(1)].P();
const Point3d p2 = points[face.PNum(2)].P();
const Point3d p3 = points[face.PNum(3)].P();
double vi = 1.0/6.0 * (p1.X() + p2.X() + p3.X()) *
( (p2.Y() - p1.Y()) * (p3.Z() - p1.Z()) -
(p2.Z() - p1.Z()) * (p3.Y() - p1.Y()) );
if (face.GetNP() == 4)
{
const Point3d p4 = points[face.PNum(4)].P();
vi += 1.0/6.0 * (p1.X() + p3.X() + p4.X()) *
( (p3.Y() - p1.Y()) * (p4.Z() - p1.Z()) -
(p3.Z() - p1.Z()) * (p4.Y() - p1.Y()) );
}
clvol[faces.Get(i).cluster] += vi;
}
NgProfiler::StopTimer (timer_c);
NgProfiler::StartTimer (timer_d);
bool negvol = false;
for (auto i : clvol.Range())
if (clvol[i] < 0)
negvol = true;
if (negvol)
{
for (int i = 1; i <= faces.Size(); i++)
faces.Elem(i).cluster = IndexBASE<PointIndex>();
// for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
for (PointIndex pi : points.Range())
points[pi].cluster = IndexBASE<PointIndex>();
}
if (hashon)
hashtable.Create();
NgProfiler::StopTimer (timer_d);
}
int AdFront3 :: SelectBaseElement ()
{
/*
static int minval = -1;
static int lasti = 0;
static int counter = 0;
*/
if (rebuildcounter <= 0)
{
RebuildInternalTables();
rebuildcounter = nff / 10 + 1;
lasti = 0;
}
rebuildcounter--;
/*
if (faces.Size() > 2 * nff)
{
// compress facelist
RebuildInternalTables ();
lasti = 0;
}
*/
int fstind = 0;
for (int i = lasti+1; i <= faces.Size() && !fstind; i++)
if (faces.Elem(i).Valid())
{
int hi = faces.Get(i).QualClass() +
points[faces.Get(i).Face().PNum(1)].FrontNr() +
points[faces.Get(i).Face().PNum(2)].FrontNr() +
points[faces.Get(i).Face().PNum(3)].FrontNr();
if (hi <= minval)
{
minval = hi;
fstind = i;
lasti = fstind;
}
}
if (!fstind)
{
minval = INT_MAX;
for (int i = 1; i <= faces.Size(); i++)
if (faces.Elem(i).Valid())
{
int hi = faces.Get(i).QualClass() +
points[faces.Get(i).Face().PNum(1)].FrontNr() +
points[faces.Get(i).Face().PNum(2)].FrontNr() +
points[faces.Get(i).Face().PNum(3)].FrontNr();
if (hi <= minval)
{
minval = hi;
fstind = i;
lasti = 0;
}
}
}
return fstind;
}
int AdFront3 :: GetLocals (int fstind,
Array<Point3d, PointIndex> & locpoints,
Array<MiniElement2d> & locfaces, // local index
Array<PointIndex, PointIndex> & pindex,
Array<INDEX> & findex,
INDEX_2_HASHTABLE<int> & getconnectedpairs,
float xh,
float relh,
INDEX& facesplit)
{
// static int timer = NgProfiler::CreateTimer ("AdFront3::GetLocals");
// NgProfiler::RegionTimer reg (timer);
if (hashon && faces.Size() < 500) { hashon=0; }
if (hashon && !hashcreated)
{
hashtable.Create();
hashcreated=1;
}
INDEX i;
PointIndex pstind;
Point3d midp, p0;
// static NgArray<int, PointIndex::BASE> invpindex;
NgArray<MiniElement2d> locfaces2; //all local faces in radius xh
NgArray<int> locfaces3; // all faces in outer radius relh
NgArray<INDEX> findex2;
locfaces2.SetSize(0);
locfaces3.SetSize(0);
findex2.SetSize(0);
PointIndex cluster = faces.Get(fstind).cluster;
pstind = faces.Get(fstind).Face().PNum(1);
p0 = points[pstind].P();
locfaces2.Append(faces.Get(fstind).Face());
findex2.Append(fstind);
Box3d b1 (p0 - Vec3d(xh, xh, xh), p0 + Vec3d (xh, xh, xh));
if (hashon)
{
hashtable.GetLocals(locfaces2, findex2, fstind, p0, xh);
}
else
{
for (i = 1; i <= faces.Size(); i++)
{
const MiniElement2d & face = faces.Get(i).Face();
if (faces.Get(i).cluster == cluster && faces.Get(i).Valid() && i != fstind)
{
Box3d b2;
b2.SetPoint (points[face[0]].P());
b2.AddPoint (points[face[1]].P());
b2.AddPoint (points[face[2]].P());
if (b1.Intersect (b2))
{
locfaces2.Append(faces.Get(i).Face());
findex2.Append(i);
}
}
}
}
//local faces for inner radius:
for (i = 1; i <= locfaces2.Size(); i++)
{
const MiniElement2d & face = locfaces2.Get(i);
const Point3d & p1 = points[face[0]].P();
const Point3d & p2 = points[face[1]].P();
const Point3d & p3 = points[face[2]].P();
midp = Center (p1, p2, p3);
if (Dist2 (midp, p0) <= relh * relh || i == 1)
{
locfaces.Append(locfaces2.Get(i));
findex.Append(findex2.Get(i));
}
else
locfaces3.Append (i);
}
facesplit=locfaces.Size();
//local faces for outer radius:
for (i = 1; i <= locfaces3.Size(); i++)
{
locfaces.Append (locfaces2.Get(locfaces3.Get(i)));
findex.Append (findex2.Get(locfaces3.Get(i)));
}
invpindex.SetSize (points.Size());
/*
for (i = 1; i <= locfaces.Size(); i++)
for (j = 1; j <= locfaces.Get(i).GetNP(); j++)
{
PointIndex pi = locfaces.Get(i).PNum(j);
invpindex[pi] = PointIndex::INVALID;
}
*/
for (auto & f : locfaces)
for (int j = 1; j <= f.GetNP(); j++)
{
PointIndex pi = f.PNum(j);
invpindex[pi] = PointIndex::INVALID;
}
// for (i = 1; i <= locfaces.Size(); i++)
for (auto & f : locfaces)
{
// for (j = 1; j <= locfaces.Get(i).GetNP(); j++)
for (int j = 1; j <= f.GetNP(); j++)
{
// PointIndex pi = locfaces.Get(i).PNum(j);
PointIndex pi = f.PNum(j);
if (!invpindex[pi].IsValid())
{
pindex.Append (pi);
locpoints.Append (points[pi].P());
invpindex[pi] = pindex.Size()-1+IndexBASE<PointIndex>();
}
// locfaces.Elem(i).PNum(j) = invpindex[pi];
f.PNum(j) = invpindex[pi];
}
}
if (connectedpairs)
{
// for (i = 1; i <= locpoints.Size(); i++)
for (auto i : locpoints.Range())
{
PointIndex pind = pindex[i]; // .Get(i);
// if (pind.IsValid() && pind <= connectedpairs->Size ())
if (connectedpairs->Range().Contains(pind))
{
// for (int j = 1; j <= connectedpairs->EntrySize(pind); j++)
for (auto j : (*connectedpairs)[pind].Range())
{
//PointIndex oi = connectedpairs->Get(pind, j);
PointIndex oi = (*connectedpairs)[pind][j];
PointIndex other = invpindex[oi];
// if (other >= 1 && other <= pindex.Size() &&
if (pindex.Range().Contains(other) &&
pindex[other] == oi)
{
// INDEX_2 coned(i, other);
// coned.Sort();
// (*testout) << "connected: " << locpoints.Get(i) << "-" << locpoints.Get(other) << endl;
getconnectedpairs.Set (PointIndices<2>::Sort (i, other), 1);
}
}
}
}
}
/*
// add isolated points
for (i = 1; i <= points.Size(); i++)
if (points.Elem(i).Valid() && Dist (points.Elem(i).P(), p0) <= xh)
{
if (!invpindex.Get(i))
{
locpoints.Append (points.Get(i).P());
pindex.Append (i);
invpindex.Elem(i) = pindex.Size();
}
}
*/
return faces.Get(fstind).QualClass();
}
// returns all points connected with fi
void AdFront3 :: GetGroup (int fi,
Array<MeshPoint, PointIndex> & grouppoints,
Array<MiniElement2d> & groupelements,
Array<PointIndex, PointIndex> & pindex,
Array<INDEX> & findex)
{
// static NgArray<char> pingroup;
int changed;
pingroup.SetSize(points.Size());
pingroup = 0;
for (int j = 1; j <= 3; j++)
pingroup[faces.Get(fi).Face().PNum(j)] = 1;
do
{
changed = 0;
/*
for (i = 1; i <= faces.Size(); i++)
if (faces.Get(i).Valid())
{
const MiniElement2d & face = faces.Get(i).Face();
int fused = 0;
for (j = 1; j <= 3; j++)
if (pingroup.Elem(face.PNum(j)))
fused++;
if (fused >= 2)
for (j = 1; j <= 3; j++)
if (!pingroup.Elem(face.PNum(j)))
{
pingroup.Elem(face.PNum(j)) = 1;
changed = 1;
}
}
*/
for (auto & f : faces)
if (f.Valid())
{
const MiniElement2d & face = f.Face();
int fused = 0;
for (int j = 1; j <= 3; j++)
if (pingroup[face.PNum(j)])
fused++;
if (fused >= 2)
for (int j = 1; j <= 3; j++)
if (!pingroup[face.PNum(j)])
{
pingroup[face.PNum(j)] = 1;
changed = 1;
}
}
}
while (changed);
invpindex.SetSize (points.Size());
// for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
for (PointIndex pi : points.Range())
if (points[pi].Valid())
{
grouppoints.Append (points[pi].P());
pindex.Append (pi);
// invpindex[pi] = pindex.Size();
invpindex[pi] = pindex.Size()-1 + IndexBASE<PointIndex>();
}
for (int i = 1; i <= faces.Size(); i++)
if (faces.Get(i).Valid())
{
int fused = 0;
for (int j = 1; j <= 3; j++)
if (pingroup[faces.Get(i).Face().PNum(j)])
fused++;
if (fused >= 2)
{
groupelements.Append (faces.Get(i).Face());
findex.Append (i);
}
}
/*
for (int i = 1; i <= groupelements.Size(); i++)
for (int j = 1; j <= 3; j++)
{
groupelements.Elem(i).PNum(j) =
invpindex.Get(groupelements.Elem(i).PNum(j));
}
*/
for (auto & e : groupelements)
for (int j = 1; j <= 3; j++)
e.PNum(j) = invpindex[e.PNum(j)];
}
void AdFront3 :: SetStartFront (int /* baseelnp */)
{
for (INDEX i = 1; i <= faces.Size(); i++)
if (faces.Get(i).Valid())
{
const MiniElement2d & face = faces.Get(i).Face();
for (int j = 1; j <= 3; j++)
points[face.PNum(j)].DecFrontNr(0);
}
/*
if (baseelnp)
{
for (i = 1; i <= faces.Size(); i++)
if (faces.Get(i).Valid() && faces.Get(i).Face().GetNP() != baseelnp)
faces.Elem(i).qualclass = 1000;
}
*/
}
bool AdFront3 :: PointInsideGroup(const Array<PointIndex, PointIndex> &grouppindex,
const Array<MiniElement2d> &groupfaces) const
{
for(auto pi : Range(points))
{
const auto& p = points[pi].P();
bool found = false;
for(const auto& f : groupfaces)
{
for(auto i : Range(3))
if(grouppindex[f.PNum(i+1)] == pi)
{
found = true;
break;
}
}
if(found)
continue;
// "random" direction
Vec<3> dir = { 0.123871, 0.15432,-0.43989 };
DenseMatrix a(3), ainv(3);
Vector b(3), u(3);
int count = 0;
for(const auto& f : groupfaces)
{
const auto& p1 = points[grouppindex[f.PNum(1)]].P();
auto v1 = points[grouppindex[f.PNum(2)]].P() - p1;
auto v2 = points[grouppindex[f.PNum(3)]].P() - p1;
for(auto i : Range(3))
{
a(i,0) = v1[i];
a(i,1) = v2[i];
a(i,2) = -dir[i];
b(i) = p[i] - p1[i];
}
CalcInverse (a, ainv);
ainv.Mult (b, u);
if (u(0) >= 0 && u(1) >= 0 && u(0)+u(1) <= 1 &&
u(2) > 0)
count++;
}
if (count % 2 == 1)
return true;
}
return false;
}
bool AdFront3 :: Inside (const Point<3> & p) const
{
static Timer timer("AdFront3::Inside"); RegionTimer rt(timer);
int cnt;
Vec3d n, v1, v2;
DenseMatrix a(3), ainv(3);
Vector b(3), u(3);
// random numbers:
n.X() = 0.123871;
n.Y() = 0.15432;
n.Z() = -0.43989;
cnt = 0;
for (int i = 1; i <= faces.Size(); i++)
if (faces.Get(i).Valid())
{
const Point<3> & p1 = points[faces.Get(i).Face().PNum(1)].P();
const Point<3> & p2 = points[faces.Get(i).Face().PNum(2)].P();
const Point<3> & p3 = points[faces.Get(i).Face().PNum(3)].P();
v1 = p2 - p1;
v2 = p3 - p1;
a(0, 0) = v1.X();
a(1, 0) = v1.Y();
a(2, 0) = v1.Z();
a(0, 1) = v2.X();
a(1, 1) = v2.Y();
a(2, 1) = v2.Z();
a(0, 2) = -n.X();
a(1, 2) = -n.Y();
a(2, 2) = -n.Z();
b(0) = p(0) - p1(0);
b(1) = p(1) - p1(1);
b(2) = p(2) - p1(2);
CalcInverse (a, ainv);
ainv.Mult (b, u);
if (u(0) >= 0 && u(1) >= 0 && u(0)+u(1) <= 1 &&
u(2) > 0)
{
cnt++;
}
}
return ((cnt % 2) != 0);
}
int AdFront3 :: SameSide (const Point<3> & lp1, const Point<3> & lp2,
const NgArray<int> * testfaces) const
{
const Point<3> *line[2];
line[0] = &lp1;
line[1] = &lp2;
Point3d pmin(lp1);
Point3d pmax(lp1);
pmin.SetToMin (lp2);
pmax.SetToMax (lp2);
NgArrayMem<int, 100> aprif;
aprif.SetSize(0);
if (!testfaces)
facetree->GetIntersecting (pmin, pmax, aprif);
else
for (int i = 1; i <= testfaces->Size(); i++)
aprif.Append (testfaces->Get(i));
int cnt = 0;
for (int ii = 1; ii <= aprif.Size(); ii++)
{
int i = aprif.Get(ii);
if (faces.Get(i).Valid())
{
const Point<3> *tri[3];
tri[0] = &points[faces.Get(i).Face().PNum(1)].P();
tri[1] = &points[faces.Get(i).Face().PNum(2)].P();
tri[2] = &points[faces.Get(i).Face().PNum(3)].P();
if (IntersectTriangleLine (&tri[0], &line[0]))
cnt++;
}
}
return ((cnt+1) % 2);
}
}