From 1d02f7f10409e596869447e6e4ffe597da62f96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20Sch=C3=B6berl?= Date: Mon, 13 Nov 2017 11:58:03 +0100 Subject: [PATCH] geometric search tree with hash-table --- libsrc/gprim/adtree.cpp | 179 ++++++++++++++++---------------- libsrc/gprim/adtree.hpp | 54 +++++----- libsrc/meshing/delaunay.cpp | 3 +- libsrc/stlgeom/stlgeomchart.cpp | 13 ++- libsrc/stlgeom/stltool.cpp | 164 ++++++++++++++++++++++++++--- libsrc/stlgeom/stltool.hpp | 5 +- 6 files changed, 283 insertions(+), 135 deletions(-) diff --git a/libsrc/gprim/adtree.cpp b/libsrc/gprim/adtree.cpp index c0aa82de..41237acc 100644 --- a/libsrc/gprim/adtree.cpp +++ b/libsrc/gprim/adtree.cpp @@ -1525,7 +1525,7 @@ namespace netgen #endif - + @@ -1795,52 +1795,49 @@ namespace netgen - template - T_ADTree :: T_ADTree (const float * acmin, - const float * acmax) - : ela(0) + template + T_ADTree :: T_ADTree (Point acmin, Point acmax) + // : ela(0) { - memcpy (cmin, acmin, dim * sizeof(float)); - memcpy (cmax, acmax, dim * sizeof(float)); + cmin = acmin; + cmax = acmax; - root = new T_ADTreeNode; + root = new T_ADTreeNode; root->sep = (cmin[0] + cmax[0]) / 2; } - template - T_ADTree :: ~T_ADTree () + template + T_ADTree :: ~T_ADTree () { root->DeleteChilds(); delete root; } - template - void T_ADTree :: Insert (const float * p, int pi) + template + void T_ADTree :: Insert (Point p, T pi) { - T_ADTreeNode *node(NULL); - T_ADTreeNode *next; + T_ADTreeNode *node(NULL); + T_ADTreeNode *next; int dir; int lr(0); - float bmin[dim]; - float bmax[dim]; + Point bmin = cmin; + Point 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; - memcpy (next->data, p, dim * sizeof(float)); + next = new T_ADTreeNode; + // 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 - void T_ADTree :: DeleteElement (int pi) + template + void T_ADTree :: DeleteElement (T pi) { - T_ADTreeNode * node = ela[pi]; - - node->pi = -1; + T_ADTreeNode * node = ela[pi]; + ela.Delete(pi); + + SetInvalid(node->pi); // = -1; node = node->father; while (node) @@ -1901,32 +1900,31 @@ namespace netgen } } - template - void T_ADTree :: PrintMemInfo (ostream & ost) const + template + void T_ADTree :: PrintMemInfo (ostream & ost) const { ost << Elements() << " elements a " << sizeof(ADTreeNode6) << " Bytes = " - << Elements() * sizeof(T_ADTreeNode) << endl; - ost << "maxind = " << ela.Size() << " = " << sizeof(T_ADTreeNode*) * ela.Size() << " Bytes" << endl; + << Elements() * sizeof(T_ADTreeNode) << endl; + ost << "maxind = " << ela.Size() << " = " << sizeof(T_ADTreeNode*) * ela.Size() << " Bytes" << endl; } - template + template class inttn { public: int dir; - T_ADTreeNode * node; + T_ADTreeNode * node; }; - template - void T_ADTree :: GetIntersecting (const float * bmin, - const float * bmax, - Array & pis) const + template + void T_ADTree :: GetIntersecting (Point bmin, Point bmax, + Array & pis) const { // static Array stack(10000); // stack.SetSize (10000); - ArrayMem,10000> stack(10000); + ArrayMem,10000> stack(10000); pis.SetSize(0); stack[0].node = root; @@ -1935,11 +1933,11 @@ namespace netgen while (stacks >= 0) { - T_ADTreeNode * node = stack[stacks].node; + T_ADTreeNode * 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 - void T_ADTree :: PrintRec (ostream & ost, const T_ADTreeNode * node) const + template + void T_ADTree :: PrintRec (ostream & ost, const T_ADTreeNode * node) const { // if (node->data) // true anyway @@ -2000,8 +1998,8 @@ namespace netgen PrintRec (ost, node->right); } - template - int T_ADTree :: DepthRec (const T_ADTreeNode * node) const + template + int T_ADTree :: DepthRec (const T_ADTreeNode * node) const { int ldepth = 0; int rdepth = 0; @@ -2013,8 +2011,8 @@ namespace netgen return 1 + max2 (ldepth, rdepth); } - template - int T_ADTree :: ElementsRec (const T_ADTreeNode * node) const + template + int T_ADTree :: ElementsRec (const T_ADTreeNode * node) const { int els = 1; if (node->left) @@ -2349,80 +2347,83 @@ namespace netgen - template - BoxTree :: BoxTree (const Box & abox) + template + BoxTree :: BoxTree (const Box & 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 - BoxTree :: BoxTree (const Point & apmin, const Point & apmax) + template + BoxTree :: BoxTree (const Point & apmin, const Point & 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 - BoxTree :: ~BoxTree () + template + BoxTree :: ~BoxTree () { delete tree; } - template - void BoxTree :: Insert (const Point & bmin, const Point & bmax, int pi) + template + void BoxTree :: Insert (const Point & bmin, const Point & 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 - void BoxTree ::GetIntersecting (const Point & pmin, const Point & pmax, - Array & pis) const + template + void BoxTree ::GetIntersecting (const Point & pmin, const Point & pmax, + Array & 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>; } diff --git a/libsrc/gprim/adtree.hpp b/libsrc/gprim/adtree.hpp index a73df57c..174e9d9a 100644 --- a/libsrc/gprim/adtree.hpp +++ b/libsrc/gprim/adtree.hpp @@ -383,19 +383,21 @@ public: -template + template class T_ADTreeNode { public: T_ADTreeNode *left, *right, *father; float sep; - float data[DIM]; - int pi; + // float data[DIM]; + Point 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 + template class T_ADTree { - T_ADTreeNode * root; - float cmin[dim], cmax[dim]; - Array*> ela; - + T_ADTreeNode * root; + // float cmin[dim], cmax[dim]; + Point cmin, cmax; + // Array*> ela; + ClosedHashTable*> ela; public: - T_ADTree (const float * acmin, - const float * acmax); + T_ADTree (Point acmin, Point acmax); ~T_ADTree (); - void Insert (const float * p, int pi); - void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; - - void DeleteElement (int pi); + void Insert (Point p, T pi); + void GetIntersecting (Point bmin, Point bmax, + Array & 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 * node) const; - int DepthRec (const T_ADTreeNode * node) const; - int ElementsRec (const T_ADTreeNode * node) const; + void PrintRec (ostream & ost, const T_ADTreeNode * node) const; + int DepthRec (const T_ADTreeNode * node) const; + int ElementsRec (const T_ADTreeNode * node) const; void PrintMemInfo (ostream & ost) const; }; @@ -551,26 +552,27 @@ public: }; - template + template class BoxTree { - T_ADTree<2*dim> * tree; + T_ADTree<2*dim,T> * tree; Point boxpmin, boxpmax; public: BoxTree (const Box & abox); BoxTree (const Point & apmin, const Point & apmax); ~BoxTree (); - void Insert (const Point & bmin, const Point & bmax, int pi); - void Insert (const Box & box, int pi) + void Insert (const Point & bmin, const Point & bmax, T pi); + void Insert (const Box & box, T pi) { Insert (box.PMin(), box.PMax(), pi); } - void DeleteElement (int pi) + void DeleteElement (T pi) { tree->DeleteElement(pi); } void GetIntersecting (const Point & pmin, const Point & pmax, - Array & pis) const; + Array & 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; }; }; } diff --git a/libsrc/meshing/delaunay.cpp b/libsrc/meshing/delaunay.cpp index dfba5a43..bf0a5174 100644 --- a/libsrc/meshing/delaunay.cpp +++ b/libsrc/meshing/delaunay.cpp @@ -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); } diff --git a/libsrc/stlgeom/stlgeomchart.cpp b/libsrc/stlgeom/stlgeomchart.cpp index b16e752a..14a7da6b 100644 --- a/libsrc/stlgeom/stlgeomchart.cpp +++ b/libsrc/stlgeom/stlgeomchart.cpp @@ -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); diff --git a/libsrc/stlgeom/stltool.cpp b/libsrc/stlgeom/stltool.cpp index 8b430504..54b8c31f 100644 --- a/libsrc/stlgeom/stltool.cpp +++ b/libsrc/stlgeom/stltool.cpp @@ -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 pis; + ArrayMem 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; diff --git a/libsrc/stlgeom/stltool.hpp b/libsrc/stlgeom/stltool.hpp index cfd0e7b2..c572c077 100644 --- a/libsrc/stlgeom/stltool.hpp +++ b/libsrc/stlgeom/stltool.hpp @@ -160,12 +160,13 @@ private: STLGeometry * geometry; const STLChart * chart; Array boundary; - BoxTree<2> * searchtree = nullptr; + ClosedHashTable 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(); } void SetChart (const STLChart * achart) { chart = achart; } //don't check, if already exists! void AddNewSegment(const STLBoundarySeg & seg) {boundary.Append(seg);};