geometric search tree with hash-table

This commit is contained in:
Joachim Schöberl 2017-11-13 11:58:03 +01:00
parent ff3c1a2c36
commit 1d02f7f104
6 changed files with 283 additions and 135 deletions

View File

@ -1525,7 +1525,7 @@ namespace netgen
#endif
@ -1795,52 +1795,49 @@ namespace netgen
template <int dim>
T_ADTree<dim> :: T_ADTree (const float * acmin,
const float * acmax)
: ela(0)
template <int dim, typename T>
T_ADTree<dim,T> :: T_ADTree (Point<dim> acmin, Point<dim> acmax)
// : ela(0)
{
memcpy (cmin, acmin, dim * sizeof(float));
memcpy (cmax, acmax, dim * sizeof(float));
cmin = acmin;
cmax = acmax;
root = new T_ADTreeNode<dim>;
root = new T_ADTreeNode<dim,T>;
root->sep = (cmin[0] + cmax[0]) / 2;
}
template <int dim>
T_ADTree<dim> :: ~T_ADTree ()
template <int dim, typename T>
T_ADTree<dim,T> :: ~T_ADTree ()
{
root->DeleteChilds();
delete root;
}
template <int dim>
void T_ADTree<dim> :: Insert (const float * p, int pi)
template <int dim, typename T>
void T_ADTree<dim,T> :: Insert (Point<dim> p, T pi)
{
T_ADTreeNode<dim> *node(NULL);
T_ADTreeNode<dim> *next;
T_ADTreeNode<dim,T> *node(NULL);
T_ADTreeNode<dim,T> *next;
int dir;
int lr(0);
float bmin[dim];
float bmax[dim];
Point<dim> bmin = cmin;
Point<dim> bmax = cmax;
memcpy (bmin, cmin, dim * sizeof(float));
memcpy (bmax, cmax, dim * sizeof(float));
next = root;
dir = 0;
while (next)
{
node = next;
if (node->pi == -1)
if (IsInvalid(node->pi))
{
memcpy (node->data, p, dim * sizeof(float));
// memcpy (node->data, p, dim * sizeof(float));
node->data = p;
node->pi = pi;
if (ela.Size() < pi+1)
ela.SetSize (pi+1);
// if (ela.Size() < pi+1)
// ela.SetSize (pi+1);
ela[pi] = node;
return;
@ -1849,13 +1846,13 @@ namespace netgen
if (node->sep > p[dir])
{
next = node->left;
bmax[dir] = node->sep;
bmax(dir) = node->sep;
lr = 0;
}
else
{
next = node->right;
bmin[dir] = node->sep;
bmin(dir) = node->sep;
lr = 1;
}
@ -1864,13 +1861,14 @@ namespace netgen
}
next = new T_ADTreeNode<dim>;
memcpy (next->data, p, dim * sizeof(float));
next = new T_ADTreeNode<dim,T>;
// memcpy (next->data, p, dim * sizeof(float));
next->data = p;
next->pi = pi;
next->sep = (bmin[dir] + bmax[dir]) / 2;
if (ela.Size() < pi+1)
ela.SetSize (pi+1);
// if (ela.Size() < pi+1)
// ela.SetSize (pi+1);
ela[pi] = next;
if (lr)
@ -1886,12 +1884,13 @@ namespace netgen
}
}
template <int dim>
void T_ADTree<dim> :: DeleteElement (int pi)
template <int dim, typename T>
void T_ADTree<dim,T> :: DeleteElement (T pi)
{
T_ADTreeNode<dim> * node = ela[pi];
node->pi = -1;
T_ADTreeNode<dim,T> * node = ela[pi];
ela.Delete(pi);
SetInvalid(node->pi); // = -1;
node = node->father;
while (node)
@ -1901,32 +1900,31 @@ namespace netgen
}
}
template <int dim>
void T_ADTree<dim> :: PrintMemInfo (ostream & ost) const
template <int dim, typename T>
void T_ADTree<dim,T> :: PrintMemInfo (ostream & ost) const
{
ost << Elements() << " elements a " << sizeof(ADTreeNode6)
<< " Bytes = "
<< Elements() * sizeof(T_ADTreeNode<dim>) << endl;
ost << "maxind = " << ela.Size() << " = " << sizeof(T_ADTreeNode<dim>*) * ela.Size() << " Bytes" << endl;
<< Elements() * sizeof(T_ADTreeNode<dim,T>) << endl;
ost << "maxind = " << ela.Size() << " = " << sizeof(T_ADTreeNode<dim,T>*) * ela.Size() << " Bytes" << endl;
}
template <int dim>
template <int dim, typename T>
class inttn {
public:
int dir;
T_ADTreeNode<dim> * node;
T_ADTreeNode<dim,T> * node;
};
template <int dim>
void T_ADTree<dim> :: GetIntersecting (const float * bmin,
const float * bmax,
Array<int> & pis) const
template <int dim, typename T>
void T_ADTree<dim,T> :: GetIntersecting (Point<dim> bmin, Point<dim> bmax,
Array<T> & pis) const
{
// static Array<inttn6> stack(10000);
// stack.SetSize (10000);
ArrayMem<inttn<dim>,10000> stack(10000);
ArrayMem<inttn<dim,T>,10000> stack(10000);
pis.SetSize(0);
stack[0].node = root;
@ -1935,11 +1933,11 @@ namespace netgen
while (stacks >= 0)
{
T_ADTreeNode<dim> * node = stack[stacks].node;
T_ADTreeNode<dim,T> * node = stack[stacks].node;
int dir = stack[stacks].dir;
stacks--;
if (node->pi != -1)
if (!IsInvalid(node->pi)) // != -1)
{
bool found = true;
for (int i = 0; i < dim/2; i++)
@ -1982,8 +1980,8 @@ namespace netgen
}
}
template <int dim>
void T_ADTree<dim> :: PrintRec (ostream & ost, const T_ADTreeNode<dim> * node) const
template <int dim, typename T>
void T_ADTree<dim,T> :: PrintRec (ostream & ost, const T_ADTreeNode<dim,T> * node) const
{
// if (node->data) // true anyway
@ -2000,8 +1998,8 @@ namespace netgen
PrintRec (ost, node->right);
}
template <int dim>
int T_ADTree<dim> :: DepthRec (const T_ADTreeNode<dim> * node) const
template <int dim, typename T>
int T_ADTree<dim,T> :: DepthRec (const T_ADTreeNode<dim,T> * node) const
{
int ldepth = 0;
int rdepth = 0;
@ -2013,8 +2011,8 @@ namespace netgen
return 1 + max2 (ldepth, rdepth);
}
template <int dim>
int T_ADTree<dim> :: ElementsRec (const T_ADTreeNode<dim> * node) const
template <int dim, typename T>
int T_ADTree<dim,T> :: ElementsRec (const T_ADTreeNode<dim,T> * node) const
{
int els = 1;
if (node->left)
@ -2349,80 +2347,83 @@ namespace netgen
template <int dim>
BoxTree<dim> :: BoxTree (const Box<dim> & abox)
template <int dim, typename T>
BoxTree<dim,T> :: BoxTree (const Box<dim> & abox)
{
boxpmin = abox.PMin();
boxpmax = abox.PMax();
float tpmin[2*dim], tpmax[2*dim];
Point<2*dim> tpmin, tpmax;
for (int i = 0; i < dim; i++)
{
tpmin[i] = tpmin[i+dim] = boxpmin(i);
tpmax[i] = tpmax[i+dim] = boxpmax(i);
tpmin(i) = tpmin(i+dim) = boxpmin(i);
tpmax(i) = tpmax(i+dim) = boxpmax(i);
}
tree = new T_ADTree<2*dim> (tpmin, tpmax);
tree = new T_ADTree<2*dim,T> (tpmin, tpmax);
}
template <int dim>
BoxTree<dim> :: BoxTree (const Point<dim> & apmin, const Point<dim> & apmax)
template <int dim, typename T>
BoxTree<dim,T> :: BoxTree (const Point<dim> & apmin, const Point<dim> & apmax)
{
boxpmin = apmin;
boxpmax = apmax;
float tpmin[2*dim], tpmax[2*dim];
Point<2*dim> tpmin, tpmax;
for (int i = 0; i < dim; i++)
{
tpmin[i] = tpmin[i+dim] = boxpmin(i);
tpmax[i] = tpmax[i+dim] = boxpmax(i);
tpmin(i) = tpmin(i+dim) = boxpmin(i);
tpmax(i) = tpmax(i+dim) = boxpmax(i);
}
tree = new T_ADTree<2*dim> (tpmin, tpmax);
tree = new T_ADTree<2*dim,T> (tpmin, tpmax);
}
template <int dim>
BoxTree<dim> :: ~BoxTree ()
template <int dim, typename T>
BoxTree<dim,T> :: ~BoxTree ()
{
delete tree;
}
template <int dim>
void BoxTree<dim> :: Insert (const Point<dim> & bmin, const Point<dim> & bmax, int pi)
template <int dim, typename T>
void BoxTree<dim,T> :: Insert (const Point<dim> & bmin, const Point<dim> & bmax, T pi)
{
float tp[2*dim];
Point<2*dim> tp;
for (int i = 0; i < dim; i++)
for (size_t i = 0; i < dim; i++)
{
tp[i] = bmin(i);
tp[i+dim] = bmax(i);
tp(i) = bmin(i);
tp(i+dim) = bmax(i);
}
tree->Insert (tp, pi);
}
template <int dim>
void BoxTree<dim> ::GetIntersecting (const Point<dim> & pmin, const Point<dim> & pmax,
Array<int> & pis) const
template <int dim, typename T>
void BoxTree<dim,T> ::GetIntersecting (const Point<dim> & pmin, const Point<dim> & pmax,
Array<T> & pis) const
{
float tpmin[2*dim];
float tpmax[2*dim];
Point<2*dim> tpmin, tpmax;
for (int i = 0; i < dim; i++)
for (size_t i = 0; i < dim; i++)
{
tpmin[i] = boxpmin(i);
tpmax[i] = pmax(i);
tpmin(i) = boxpmin(i);
tpmax(i) = pmax(i);
tpmin[i+dim] = pmin(i);
tpmax[i+dim] = boxpmax(i);
tpmin(i+dim) = pmin(i);
tpmax(i+dim) = boxpmax(i);
}
tree->GetIntersecting (tpmin, tpmax, pis);
}
template<> BlockAllocator T_ADTreeNode<4> :: ball(sizeof (T_ADTreeNode<4>));
template class T_ADTree<4>;
template class BoxTree<2>;
template<> BlockAllocator T_ADTreeNode<4,INDEX> :: ball(sizeof (T_ADTreeNode<4,INDEX>));
template class T_ADTree<4,INDEX>;
template class BoxTree<2,INDEX>;
template<> BlockAllocator T_ADTreeNode<4,INDEX_2> :: ball(sizeof (T_ADTreeNode<4,INDEX_2>));
template class T_ADTree<4,INDEX_2>;
template class BoxTree<2,INDEX_2>;
template<> BlockAllocator T_ADTreeNode<6> :: ball(sizeof (T_ADTreeNode<6>));
template class T_ADTree<6>;
template class BoxTree<3>;
template<> BlockAllocator T_ADTreeNode<6,INDEX> :: ball(sizeof (T_ADTreeNode<6,INDEX>));
template class T_ADTree<6,INDEX>;
template class BoxTree<3,INDEX>;
}

View File

@ -383,19 +383,21 @@ public:
template <int DIM>
template <int DIM, typename T>
class T_ADTreeNode
{
public:
T_ADTreeNode *left, *right, *father;
float sep;
float data[DIM];
int pi;
// float data[DIM];
Point<DIM,float> data;
T pi;
int nchilds;
T_ADTreeNode ()
{
pi = -1;
// pi = -1;
SetInvalid(pi);
left = NULL;
right = NULL;
father = NULL;
@ -435,24 +437,23 @@ public:
template <int dim>
template <int dim, typename T = INDEX>
class T_ADTree
{
T_ADTreeNode<dim> * root;
float cmin[dim], cmax[dim];
Array<T_ADTreeNode<dim>*> ela;
T_ADTreeNode<dim,T> * root;
// float cmin[dim], cmax[dim];
Point<dim> cmin, cmax;
// Array<T_ADTreeNode<dim>*> ela;
ClosedHashTable<T, T_ADTreeNode<dim,T>*> ela;
public:
T_ADTree (const float * acmin,
const float * acmax);
T_ADTree (Point<dim> acmin, Point<dim> acmax);
~T_ADTree ();
void Insert (const float * p, int pi);
void GetIntersecting (const float * bmin, const float * bmax,
Array<int> & pis) const;
void DeleteElement (int pi);
void Insert (Point<dim> p, T pi);
void GetIntersecting (Point<dim> bmin, Point<dim> bmax,
Array<T> & pis) const;
void DeleteElement (T pi);
void Print (ostream & ost) const
{ PrintRec (ost, root); }
@ -461,9 +462,9 @@ public:
int Elements () const
{ return ElementsRec (root); }
void PrintRec (ostream & ost, const T_ADTreeNode<dim> * node) const;
int DepthRec (const T_ADTreeNode<dim> * node) const;
int ElementsRec (const T_ADTreeNode<dim> * node) const;
void PrintRec (ostream & ost, const T_ADTreeNode<dim,T> * node) const;
int DepthRec (const T_ADTreeNode<dim,T> * node) const;
int ElementsRec (const T_ADTreeNode<dim,T> * node) const;
void PrintMemInfo (ostream & ost) const;
};
@ -551,26 +552,27 @@ public:
};
template <int dim>
template <int dim, typename T = INDEX>
class BoxTree
{
T_ADTree<2*dim> * tree;
T_ADTree<2*dim,T> * tree;
Point<dim> boxpmin, boxpmax;
public:
BoxTree (const Box<dim> & abox);
BoxTree (const Point<dim> & apmin, const Point<dim> & apmax);
~BoxTree ();
void Insert (const Point<dim> & bmin, const Point<dim> & bmax, int pi);
void Insert (const Box<dim> & box, int pi)
void Insert (const Point<dim> & bmin, const Point<dim> & bmax, T pi);
void Insert (const Box<dim> & box, T pi)
{
Insert (box.PMin(), box.PMax(), pi);
}
void DeleteElement (int pi)
void DeleteElement (T pi)
{ tree->DeleteElement(pi); }
void GetIntersecting (const Point<dim> & pmin, const Point<dim> & pmax,
Array<int> & pis) const;
Array<T> & pis) const;
const T_ADTree<2*dim> & Tree() const { return *tree; };
// const T_ADTree<2*dim> & Tree() const { return *tree; };
// T_ADTree<2*dim> & Tree() { return *tree; };
};
}

View File

@ -503,7 +503,8 @@ namespace netgen
for (int k = 0; k < 4; k++)
tempels.Elem(celind)[k] = -1;
((ADTree6&)tettree.Tree()).DeleteElement (celind);
// ((ADTree6&)tettree.Tree()).DeleteElement (celind);
tettree.DeleteElement (celind);
freelist.Append (celind);
}

View File

@ -22,13 +22,14 @@ void STLGeometry :: MakeAtlas(Mesh & mesh)
int timer1 = NgProfiler::CreateTimer ("makeatlas");
int timer2 = NgProfiler::CreateTimer ("makeatlas - part 2");
int timer3 = NgProfiler::CreateTimer ("makeatlas - part 3");
/*
int timer4 = NgProfiler::CreateTimer ("makeatlas - part 4");
int timer5 = NgProfiler::CreateTimer ("makeatlas - part 5");
int timer5a = NgProfiler::CreateTimer ("makeatlas - part 5a");
int timer5b = NgProfiler::CreateTimer ("makeatlas - part 5b");
int timer5cs = NgProfiler::CreateTimer ("makeatlas - part 5cs");
int timer5cl = NgProfiler::CreateTimer ("makeatlas - part 5cl");
*/
PushStatusF("Make Atlas");
double h = mparam.maxh;
@ -139,6 +140,8 @@ void STLGeometry :: MakeAtlas(Mesh & mesh)
innerchartpts.SetSize(0);
chartbound.Clear();
chartbound.SetChart(chart);
chartbound.BuildSearchTree(); // different !!!
if (!found) { PrintSysError("Make Atlas, no starttrig found"); return; }
@ -288,7 +291,7 @@ void STLGeometry :: MakeAtlas(Mesh & mesh)
innerchartpts.SetSize(innerchartpoints.Size());
for (size_t i = 0; i < innerchartpoints.Size(); i++)
innerchartpts[i] = GetPoint(innerchartpoints[i]);
chartbound.BuildSearchTree();
// chartbound.BuildSearchTree(); // different !!!
NgProfiler::StopTimer (timer2);
NgProfiler::StartTimer (timer3);
@ -461,8 +464,6 @@ void STLGeometry :: MakeAtlas(Mesh & mesh)
}
}
chartbound.DeleteSearchTree();
NgProfiler::StopTimer (timer3);
//end of while loop for outer chart
@ -490,6 +491,10 @@ void STLGeometry :: MakeAtlas(Mesh & mesh)
lastunmarked = prelastunmarked;
}
chartbound.DeleteSearchTree();
// cout << "key" << endl;
// char key;
// cin >> key;
//calculate an estimate meshsize, not to produce too large outercharts, with factor 2 larger!
RestrictHChartDistOneChart(chartnum, chartdistacttrigs, mesh, h, 0.5, atlasminh);
// NgProfiler::Print(stdout);

View File

@ -625,6 +625,9 @@ STLChart :: STLChart(STLGeometry * ageometry)
void STLChart :: AddChartTrig(int i)
{
// static int timer = NgProfiler::CreateTimer ("STLChart::AddChartTrig");
// NgProfiler::RegionTimer reg(timer);
charttrigs->Append(i);
const STLTriangle & trig = geometry->GetTriangle(i);
@ -644,6 +647,9 @@ void STLChart :: AddChartTrig(int i)
void STLChart :: AddOuterTrig(int i)
{
// static int timer = NgProfiler::CreateTimer ("STLChart::AddOuterTrig");
// NgProfiler::RegionTimer reg(timer);
outertrigs->Append(i);
const STLTriangle & trig = geometry->GetTriangle(i);
@ -853,6 +859,12 @@ void STLBoundary :: AddOrDelSegment(const STLBoundarySeg & seg)
void STLBoundary ::AddTriangle(const STLTriangle & t)
{
// static int timer_old = NgProfiler::CreateTimer ("STLChart::AddTriangle_old");
// static int timer_new = NgProfiler::CreateTimer ("STLChart::AddTriangle_new");
// NgProfiler::StartTimer (timer_old);
#ifdef ADDTRIGOLD
int i;
int found1 = 0;
int found2 = 0;
@ -892,9 +904,127 @@ void STLBoundary ::AddTriangle(const STLTriangle & t)
{ boundary.DeleteElement (i); found3 = 1; }
}
if (!found1) {seg1.Swap(); boundary.Append(seg1);}
if (!found2) {seg2.Swap(); boundary.Append(seg2);}
if (!found3) {seg3.Swap(); boundary.Append(seg3);}
if (!found1)
{
seg1.Swap();
boundary.Append(seg1);
/*
int newnr;
if (freelist.Size())
{
newnr = freelist.Last();
freelist.DeleteLast();
boundary[newnr] = seg1;
}
else
{
boundary.Append(seg1);
newnr = boundary.Size();
}
// cout << "tree add el " << boundary.Size() << endl;
if (searchtree)
{
// cout << "add " << boundary.Size() << endl;
searchtree->Insert (seg1.BoundingBox(), newnr);
}
*/
}
if (!found2)
{
seg2.Swap();
boundary.Append(seg2);
/*
int newnr;
if (freelist.Size())
{
newnr = freelist.Last();
freelist.DeleteLast();
boundary[newnr] = seg2;
}
else
{
boundary.Append(seg2);
newnr = boundary.Size();
}
// boundary.Append(seg2);
// cout << "tree add el " << boundary.Size() << endl;
if (searchtree)
{
// cout << "add " << boundary.Size() << endl;
searchtree->Insert (seg2.BoundingBox(), newnr);
}
*/
}
if (!found3)
{
seg3.Swap();
boundary.Append(seg3);
/*
int newnr;
if (freelist.Size())
{
newnr = freelist.Last();
freelist.DeleteLast();
boundary[newnr] = seg3;
}
else
{
boundary.Append(seg3);
newnr = boundary.Size();
}
// cout << "tree add el " << boundary.Size() << endl;
if (searchtree)
{
// cout << "add " << boundary.Size() << endl;
searchtree->Insert (seg3.BoundingBox(), newnr);
}
*/
}
#endif
// NgProfiler::StopTimer (timer_old);
// NgProfiler::StartTimer (timer_new);
INDEX_2 segs[3];
segs[0] = INDEX_2(t[0], t[1]);
segs[1] = INDEX_2(t[1], t[2]);
segs[2] = INDEX_2(t[2], t[0]);
for (auto seg : segs)
{
STLBoundarySeg bseg(seg[0], seg[1], geometry->GetPoints(), chart);
bseg.SetSmoothEdge (geometry->IsSmoothEdge (seg[0],seg[1]));
INDEX_2 op(seg[1], seg[0]);
if (boundary_ht.Used(op))
{
// cout << "delete " << op << endl;
boundary_ht.Delete(op);
}
else
{
// cout << "insert " << seg << endl;
boundary_ht[seg] = bseg;
}
}
/*
// cout << "bounds = " << boundary << endl;
cout << "bounds:";
for (auto & val : boundary)
cout << val.I1() << "-" << val.I2() << endl;
cout << "ht = " << boundary_ht << endl;
if (boundary_ht.UsedElements() != boundary.Size())
{
cout << "wrong count" << endl;
char key;
cin >> key;
}
*/
// NgProfiler::StopTimer (timer_new);
}
int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> & sn,
@ -1090,11 +1220,12 @@ int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3>
void STLBoundary :: BuildSearchTree()
{
static int timer = NgProfiler::CreateTimer ("BuildSearchTree");
NgProfiler::RegionTimer reg(timer);
// static int timer = NgProfiler::CreateTimer ("BuildSearchTree");
// NgProfiler::RegionTimer reg(timer);
delete searchtree;
/*
Box<2> box2d(Box<2>::EMPTY_BOX);
int nseg = NOSegments();
@ -1114,12 +1245,18 @@ void STLBoundary :: BuildSearchTree()
if (seg.IsSmoothEdge()) continue;
searchtree -> Insert (seg.BoundingBox(), j);
}
*/
Box<2> box2d(Box<2>::EMPTY_BOX);
Box<3> box3d = geometry->GetBoundingBox();
for (size_t i = 0; i < 8; i++)
box2d.Add ( chart->Project2d (box3d.GetPointNr(i)));
searchtree = new BoxTree<2,INDEX_2> (box2d);
}
void STLBoundary :: DeleteSearchTree()
{
static int timer = NgProfiler::CreateTimer ("DeleteSearchTree");
NgProfiler::RegionTimer reg(timer);
// static int timer = NgProfiler::CreateTimer ("DeleteSearchTree");
// NgProfiler::RegionTimer reg(timer);
delete searchtree;
searchtree = nullptr;
@ -1129,11 +1266,11 @@ void STLBoundary :: DeleteSearchTree()
int STLBoundary :: TestSegChartNV(const Point3d & p1, const Point3d& p2,
const Vec3d& sn)
{
static int timerquick = NgProfiler::CreateTimer ("TestSegChartNV-searchtree");
static int timer = NgProfiler::CreateTimer ("TestSegChartNV");
// static int timerquick = NgProfiler::CreateTimer ("TestSegChartNV-searchtree");
// static int timer = NgProfiler::CreateTimer ("TestSegChartNV");
int nseg = NOSegments();
Point<2> p2d1 = chart->Project2d (p1);
Point<2> p2d2 = chart->Project2d (p2);
@ -1159,12 +1296,13 @@ int STLBoundary :: TestSegChartNV(const Point3d & p1, const Point3d& p2,
{
// NgProfiler::RegionTimer reg(timerquick);
ArrayMem<int,100> pis;
ArrayMem<INDEX_2,100> pis;
searchtree -> GetIntersecting (box2d.PMin(), box2d.PMax(), pis);
for (int j : pis)
for (auto i2 : pis)
{
const STLBoundarySeg & seg = GetSegment(j);
// const STLBoundarySeg & seg = GetSegment(j);
const STLBoundarySeg & seg = boundary_ht[i2];
if (seg.IsSmoothEdge()) continue;
if (!box2d.Intersect (seg.BoundingBox())) continue;

View File

@ -160,12 +160,13 @@ private:
STLGeometry * geometry;
const STLChart * chart;
Array<STLBoundarySeg> boundary;
BoxTree<2> * searchtree = nullptr;
ClosedHashTable<INDEX_2, STLBoundarySeg> boundary_ht;
BoxTree<2,INDEX_2> * searchtree = nullptr;
public:
STLBoundary(STLGeometry * ageometry);
~STLBoundary() { delete searchtree; }
void Clear() {boundary.SetSize(0);};
void Clear() {boundary.SetSize(0); boundary_ht = ClosedHashTable<INDEX_2,STLBoundarySeg>(); }
void SetChart (const STLChart * achart) { chart = achart; }
//don't check, if already exists!
void AddNewSegment(const STLBoundarySeg & seg) {boundary.Append(seg);};