//20.11.1999 third part of stlgeom.cc, functions with chart and atlas #include #include #include #include #include #include "stlgeom.hpp" namespace netgen { int chartdebug = 0; void STLGeometry :: MakeAtlas(Mesh & mesh, const MeshingParameters& mparam, const STLParameters& stlparam) { static Timer t("makeatlas"); RegionTimer reg(t); static Timer tinner("find innner chart"); static Timer touter("find outer chart"); // int timer1 = NgProfiler::CreateTimer ("makeatlas"); /* int timerb = NgProfiler::CreateTimer ("makeatlas - begin"); int timere = NgProfiler::CreateTimer ("makeatlas - end"); int timere1 = NgProfiler::CreateTimer ("makeatlas - end1"); int timere2 = NgProfiler::CreateTimer ("makeatlas - end2"); int timer2 = NgProfiler::CreateTimer ("makeatlas - part 2"); int timer3 = NgProfiler::CreateTimer ("makeatlas - part 3"); int timer4 = NgProfiler::CreateTimer ("makeatlas - part 4"); int timer4a = NgProfiler::CreateTimer ("makeatlas - part 4a"); int timer4b = NgProfiler::CreateTimer ("makeatlas - part 4b"); int timer4c = NgProfiler::CreateTimer ("makeatlas - part 4c"); int timer4d = NgProfiler::CreateTimer ("makeatlas - part 4d"); int timer4e = NgProfiler::CreateTimer ("makeatlas - part 4e"); 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; double atlasminh = 5e-3 * Dist (boundingbox.PMin(), boundingbox.PMax()); PrintMessage(5, "atlasminh = ", atlasminh); //speedup for make atlas if (GetNT() > 50000) mesh.SetGlobalH(min2 (0.05*Dist (boundingbox.PMin(), boundingbox.PMax()), mparam.maxh)); atlas.SetSize(0); ClearSpiralPoints(); BuildSmoothEdges(); // NgProfiler::StartTimer (timer1); double chartangle = stlparam.chartangle; double outerchartangle = stlparam.outerchartangle; chartangle = chartangle/180.*M_PI; outerchartangle = outerchartangle/180.*M_PI; double coschartangle = cos(chartangle); double cosouterchartangle = cos(outerchartangle); double cosouterchartanglehalf = cos(0.5*outerchartangle); double sinchartangle = sin(chartangle); double sinouterchartangle = sin(outerchartangle); Array outermark(GetNT()); //marks all trigs form actual outer region Array outertested(GetNT()); //marks tested trigs for outer region Array pointstochart(GetNP()); //point in chart becomes chartnum Array innerpointstochart(GetNP()); //point in chart becomes chartnum Array chartpoints; //point in chart becomes chartnum Array innerchartpoints; Array> innerchartpts; NgArray dirtycharttrigs; NgArray chartdistacttrigs (GetNT()); //outercharttrigs chartdistacttrigs = 0; STLBoundary chartbound(this); //knows the actual chart boundary //int chartboundarydivisions = 10; markedsegs.SetSize(0); //for testing!!! NgArray chartpointchecked(GetNP()); //for dirty-chart-trigs chartmark.SetSize(GetNT()); innerpointstochart = ChartId::INVALID; pointstochart = ChartId::INVALID; chartpointchecked = ChartId::INVALID; double eps = 1e-12 * Dist (boundingbox.PMin(), boundingbox.PMax()); int spiralcheckon = stldoctor.spiralcheck; if (!spiralcheckon) {PrintWarning("++++++++++++\nspiral deactivated by user!!!!\n+++++++++++++++"); } chartmark = ChartId::INVALID; outermark = ChartId::INVALID; outertested = ChartId::INVALID; double atlasarea = Area(); double workedarea = 0; double showinc = 100.*5000./(double)GetNT(); double nextshow = 0; STLTrigId lastunmarked = 1; PrintMessage(5,"one dot per 5000 triangles: "); size_t markedtrigcnt = 0; while (markedtrigcnt < GetNT()) { if (multithread.terminate) { PopStatus(); return; } // NgProfiler::StartTimer (timerb); if (workedarea / atlasarea*100. >= nextshow) {PrintDot(); nextshow+=showinc;} SetThreadPercent(100.0 * workedarea / atlasarea); atlas.Append (make_unique (this, stlparam)); STLChart & chart = *atlas.Last(); //find unmarked trig STLTrigId prelastunmarked = lastunmarked; bool found = false; for (STLTrigId j = lastunmarked; j <= GetNT(); j++) if (!GetMarker(j)) { found = true; lastunmarked = j; break; } chartpoints.SetSize(0); innerchartpoints.SetSize(0); innerchartpts.SetSize(0); chartbound.Clear(); chartbound.SetChart(&chart); chartbound.BuildSearchTree(); // different !!! if (!found) throw Exception("Make Atlas, no starttrig found"); //find surrounding trigs // int starttrig = j; STLTrigId starttrig = lastunmarked; Point<3> startp = GetPoint(GetTriangle(starttrig)[0]); bool accepted; ChartId chartnum = GetNOCharts(); Vec<3> sn = GetTriangle(starttrig).Normal(); chart.SetNormal (startp, sn); // *testout << "first trig " << starttrig << ", n = " << sn << endl; SetMarker(starttrig, chartnum); markedtrigcnt++; chart.AddChartTrig(starttrig); chartbound.AddTriangle(GetTriangle(starttrig)); workedarea += GetTriangle(starttrig).Area(points); for (int i = 0; i < 3; i++) { STLPointId pi = GetTriangle(starttrig)[i]; innerpointstochart[pi] = chartnum; pointstochart[pi] = chartnum; chartpoints.Append(pi); innerchartpoints.Append(pi); } bool changed = true; int oldstartic = 1; int oldstartic2; // NgProfiler::StopTimer (timerb); // NgProfiler::StartTimer (timer2); tinner.Start(); while (changed) { changed = false; oldstartic2 = oldstartic; oldstartic = chart.GetNT(); // for (ic = oldstartic2; ic <= chart->GetNT(); ic++) for (int ic = oldstartic2; ic <= oldstartic; ic++) { STLTrigId i = chart.GetTrig1(ic); if (GetMarker(i) == chartnum) { for (int j = 1; j <= NONeighbourTrigs(i); j++) { STLTrigId nt = NeighbourTrig(i,j); // *testout << "check trig " << nt << endl; STLPointId np1, np2; GetTriangle(i).GetNeighbourPoints(GetTriangle(nt),np1,np2); if (GetMarker(nt) == 0 && !IsEdge(np1,np2)) { Vec<3> n2 = GetTriangle(nt).Normal(); // *testout << "acos = " << 180/M_PI*acos (n2*sn) << endl; if ( (n2 * sn) >= coschartangle ) { // *testout << "good angle " << endl; accepted = true; /* //alter spiralentest, schnell, aber ungenau for (k = 1; k <= 3; k++) { //find overlapping charts: Point3d pt = GetPoint(GetTriangle(nt).PNum(k)); if (innerpointstochart.Get(GetTriangle(nt).PNum(k)) != chartnum) { for (l = 1; l <= chartpoints.Size(); l++) { Vec3d vptpl(GetPoint(chartpoints.Get(l)), pt); double vlen = vptpl.Length(); if (vlen > 0) { vptpl /= vlen; if ( fabs( vptpl * sn) > sinchartangle ) { accepted = 0; break; } } } } } */ //find overlapping charts exacter (fast, too): for (int k = 1; k <= 3; k++) { int nnt = NeighbourTrig(nt,k); if (GetMarker(nnt) != chartnum) { STLPointId nnp1, nnp2; GetTriangle(nt).GetNeighbourPoints(GetTriangle(nnt),nnp1,nnp2); accepted = chartbound.TestSeg(GetPoint(nnp1), GetPoint(nnp2), sn,sinchartangle,1 /*chartboundarydivisions*/ ,points, eps); // if (!accepted) *testout << "not acc due to testseg" << endl; Vec<3> n3 = GetTriangle(nnt).Normal(); if ( (n3 * sn) >= coschartangle && IsSmoothEdge (nnp1, nnp2) ) accepted = true; } if (!accepted) break; } /* // new check 2019-09-22 if (accepted) { auto & trig = GetTriangle(nt); Point<3> p0 = GetPoint(trig[0]); Point<3> p1 = GetPoint(trig[1]); Point<3> p2 = GetPoint(trig[2]); Point<3> p01 = Center(p0,p1); Point<3> p02 = Center(p0,p2); Point<3> p12 = Center(p1,p2); Point<3> p012 = Center(p0,p1,p2); p01 += 1e-5 * (p012-p01); p02 += 1e-5 * (p012-p02); p12 += 1e-5 * (p012-p12); bool test1 = chartbound.TestSegChartNV(p01,p012,sn); bool test2 = chartbound.TestSegChartNV(p02,p012,sn); bool test3 = chartbound.TestSegChartNV(p12,p012,sn); if (!test1 || !test2 || !test3) { cout << "more stringent" << endl; accepted = false; } } */ if (accepted) { // *testout << "trig accepted" << endl; SetMarker(nt, chartnum); changed = true; markedtrigcnt++; workedarea += GetTriangle(nt).Area(points); chart.AddChartTrig(nt); chartbound.AddTriangle(GetTriangle(nt)); for (int k = 1; k <= 3; k++) { STLPointId pi = GetTriangle(nt).PNum(k); if (innerpointstochart[pi] != chartnum) { innerpointstochart[pi] = chartnum; pointstochart[pi] = chartnum; chartpoints.Append(pi); innerchartpoints.Append(pi); } } } } } } } } } tinner.Stop(); innerchartpts.SetSize(innerchartpoints.Size()); for (size_t i = 0; i < innerchartpoints.Size(); i++) innerchartpts[i] = GetPoint(innerchartpoints[i]); // chartbound.BuildSearchTree(); // different !!! // NgProfiler::StopTimer (timer2); // NgProfiler::StartTimer (timer3); //find outertrigs // chartbound.Clear(); // warum, ic-bound auf edge macht Probleme js ??? touter.Start(); outermark[starttrig] = chartnum; //chart->AddOuterTrig(starttrig); changed = true; oldstartic = 1; while (changed) { changed = false; oldstartic2 = oldstartic; oldstartic = chart.GetNT(); for (int ic = oldstartic2; ic <= oldstartic; ic++) { STLTrigId i = chart.GetTrig1(ic); if (outermark[i] != chartnum) continue; for (int j = 1; j <= NONeighbourTrigs(i); j++) { STLTrigId nt = NeighbourTrig(i,j); if (outermark[nt] == chartnum) continue; const STLTriangle & ntrig = GetTriangle(nt); STLPointId np1, np2; GetTriangle(i).GetNeighbourPoints(GetTriangle(nt),np1,np2); if (IsEdge (np1, np2)) continue; /* if (outertested.Get(nt) == chartnum) continue; */ outertested[nt] = chartnum; Vec<3> n2 = GetTriangle(nt).Normal(); //abfragen, ob noch im tolerierten Winkel if ( (n2 * sn) >= cosouterchartangle ) { accepted = true; // NgProfiler::StartTimer (timer4); bool isdirtytrig = false; Vec<3> gn = GetTriangle(nt).GeomNormal(points); double gnlen = gn.Length(); if (n2 * gn <= cosouterchartanglehalf * gnlen) isdirtytrig = true; //zurueckweisen, falls eine Spiralartige outerchart entsteht //find overlapping charts exacter: //do not check dirty trigs! // NgProfiler::StartTimer (timer4a); if (spiralcheckon && !isdirtytrig) for (int k = 1; k <= 3; k++) { // NgProfiler::StartTimer (timer4b); STLTrigId nnt = NeighbourTrig(nt,k); if (outermark[nnt] != chartnum) { // NgProfiler::StartTimer (timer4c); STLPointId nnp1, nnp2; GetTriangle(nt).GetNeighbourPoints(GetTriangle(nnt),nnp1,nnp2); // NgProfiler::StopTimer (timer4c); // NgProfiler::StartTimer (timer4d); accepted = chartbound.TestSeg(GetPoint(nnp1),GetPoint(nnp2), sn,sinouterchartangle, 0 /*chartboundarydivisions*/ ,points, eps); // NgProfiler::StopTimer (timer4d); // NgProfiler::StartTimer (timer4e); Vec<3> n3 = GetTriangle(nnt).Normal(); if ( (n3 * sn) >= cosouterchartangle && IsSmoothEdge (nnp1, nnp2) ) accepted = true; // NgProfiler::StopTimer (timer4e); } // NgProfiler::StopTimer (timer4b); if (!accepted) break; } // NgProfiler::StopTimer (timer4a); // NgProfiler::StopTimer (timer4); // NgProfiler::RegionTimer reg5(timer5); // outer chart is only small environment of // inner chart: if (accepted) { // NgProfiler::StartTimer (timer5a); accepted = false; for (int k = 1; k <= 3; k++) if (innerpointstochart[ntrig.PNum(k)] == chartnum) { accepted = true; break; } // NgProfiler::StopTimer (timer5a); // int timer5csl = (innerchartpts.Size() < 100) ? timer5cs : timer5cl; // NgProfiler::StartTimer (timer5csl); if (!accepted) for (int k = 1; k <= 3; k++) { Point<3> pt = GetPoint(ntrig.PNum(k)); double h2 = sqr(mesh.GetH(pt)); /* for (int l = 1; l <= innerchartpoints.Size(); l++) { double tdist = Dist2(pt, GetPoint (innerchartpoints.Get(l))); if (tdist < 4 * h2) { accepted = 1; break; } } */ for (int l = 0; l < innerchartpts.Size(); l++) { double tdist = Dist2(pt, innerchartpts[l]); if (tdist < 4 * h2) { accepted = true; break; } } if (accepted) break; } // NgProfiler::StopTimer (timer5csl); } // NgProfiler::StartTimer (timer5b); if (accepted) { changed = true; outermark[nt] = chartnum; if (GetMarker(nt) != chartnum) { chartbound.AddTriangle(GetTriangle(nt)); chart.AddOuterTrig(nt); for (int k = 1; k <= 3; k++) { if (pointstochart[GetTriangle(nt).PNum(k)] != chartnum) { pointstochart[GetTriangle(nt).PNum(k)] = chartnum; chartpoints.Append(GetTriangle(nt).PNum(k)); } } } } // NgProfiler::StopTimer (timer5b); } } } } touter.Stop(); // NgProfiler::StopTimer (timer3); // NgProfiler::StartTimer (timere); // NgProfiler::StartTimer (timere1); //end of while loop for outer chart GetDirtyChartTrigs(chartnum, chart, outermark, chartpointchecked, dirtycharttrigs); //dirtycharttrigs are local (chart) point numbers!!!!!!!!!!!!!!!! if (dirtycharttrigs.Size() != 0 && (dirtycharttrigs.Size() != chart.GetNChartT() || dirtycharttrigs.Size() != 1)) { if (dirtycharttrigs.Size() == chart.GetNChartT() && dirtycharttrigs.Size() != 1) { //if all trigs would be eliminated -> leave 1 trig! dirtycharttrigs.SetSize(dirtycharttrigs.Size() - 1); } for (int k = 1; k <= dirtycharttrigs.Size(); k++) { STLTrigId tn = chart.GetChartTrig1(dirtycharttrigs.Get(k)); outermark[tn] = 0; //not necessary, for later use SetMarker(tn, 0); markedtrigcnt--; workedarea -= GetTriangle(tn).Area(points); } chart.MoveToOuterChart(dirtycharttrigs); lastunmarked = 1; lastunmarked = prelastunmarked; } chartbound.DeleteSearchTree(); // NgProfiler::StopTimer (timere1); // NgProfiler::StartTimer (timere2); // 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, stlparam); // NgProfiler::Print(stdout); // NgProfiler::StopTimer (timere2); // NgProfiler::StopTimer (timere); } // NgProfiler::StopTimer (timer1); // NgProfiler::Print(stdout); PrintMessage(5,""); PrintMessage(5,"NO charts=", atlas.Size()); int cnttrias = 0; outerchartspertrig.SetSize(GetNT()); // for (int i = 1; i <= atlas.Size(); i++) for (ChartId i : atlas.Range()) { for (int j = 1; j <= GetChart(i).GetNT(); j++) { STLTrigId tn = GetChart(i).GetTrig1(j); AddOCPT(tn,i); } cnttrias += GetChart(i).GetNT(); } PrintMessage(5, "NO outer chart trias=", cnttrias); //sort outerchartspertrig for (int i = 1; i <= GetNT(); i++) { for (int k = 1; k < GetNOCPT(i); k++) for (int j = 1; j < GetNOCPT(i); j++) { int swap = GetOCPT(i,j); if (GetOCPT(i,j+1) < swap) { SetOCPT(i,j,GetOCPT(i,j+1)); SetOCPT(i,j+1,swap); } } // check make atlas if (GetChartNr(i) <= 0 || GetChartNr(i) > GetNOCharts()) PrintSysError("Make Atlas: chartnr(", i, ")=0!!"); } mesh.SetGlobalH(mparam.maxh); mesh.SetMinimalH(mparam.minh); AddConeAndSpiralEdges(stlparam); PrintMessage(5,"Make Atlas finished"); PopStatus(); } int STLGeometry::TrigIsInOC(int tn, int ocn) const { if (tn < 1 || tn > GetNT()) { // assert (1); abort (); PrintSysError("STLGeometry::TrigIsInOC illegal tn: ", tn); return 0; } /* int firstval = 0; int i; for (i = 1; i <= GetNOCPT(tn); i++) { if (GetOCPT(tn, i) == ocn) {firstval = 1;} } */ int found = 0; int inc = 1; while (inc <= GetNOCPT(tn)) {inc *= 2;} inc /= 2; int start = inc; while (!found && inc > 0) { if (GetOCPT(tn,start) > ocn) {inc = inc/2; start -= inc;} else if (GetOCPT(tn,start) < ocn) {inc = inc/2; if (start+inc <= GetNOCPT(tn)) {start += inc;}} else {found = 1;} } return GetOCPT(tn, start) == ocn; } ChartId STLGeometry :: GetChartNr(STLTrigId i) const { if (i > chartmark.Size()) { PrintSysError("GetChartNr(", int(i), ") not possible!!!"); i = 1; } return chartmark[i]; } /* int STLGeometry :: GetMarker(int i) const { return chartmark.Get(i); } */ void STLGeometry :: SetMarker(STLTrigId nr, ChartId m) { chartmark[nr] = m; } int STLGeometry :: AtlasMade() const { return chartmark.Size() != 0; } /* //return 1 if not exists int AddIfNotExists(NgArray& list, int x) { int i; for (i = 1; i <= list.Size(); i++) { if (list.Get(i) == x) {return 0;} } list.Append(x); return 1; } */ void STLGeometry :: GetInnerChartLimes(NgArray& limes, ChartId chartnum) { STLPointId np1, np2; limes.SetSize(0); STLChart& chart = GetChart(chartnum); for (int j = 1; j <= chart.GetNChartT(); j++) { STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); for (int k = 1; k <= 3; k++) { STLTrigId nt = NeighbourTrig(t,k); if (GetChartNr(nt) != chartnum) { tt.GetNeighbourPoints(GetTriangle(nt),np1,np2); if (!IsEdge(np1,np2)) { limes.Append(twoint(np1,np2)); /* p3p1 = GetPoint(np1); p3p2 = GetPoint(np2); if (AddIfNotExists(limes,np1)) { plimes1.Append(p3p1); //plimes1trigs.Append(t); //plimes1origin.Append(np1); } if (AddIfNotExists(limes1,np2)) { plimes1.Append(p3p2); //plimes1trigs.Append(t); //plimes1origin.Append(np2); } //chart.AddILimit(twoint(np1,np2)); for (int di = 1; di <= divisions; di++) { double f1 = (double)di/(double)(divisions+1.); double f2 = (divisions+1.-(double)di)/(double)(divisions+1.); plimes1.Append(Point3d(p3p1.X()*f1+p3p2.X()*f2, p3p1.Y()*f1+p3p2.Y()*f2, p3p1.Z()*f1+p3p2.Z()*f2)); //plimes1trigs.Append(t); //plimes1origin.Append(0); } */ } } } } } void STLGeometry :: GetDirtyChartTrigs(int chartnum, STLChart& chart, const Array& outercharttrigs, NgArray& chartpointchecked, NgArray& dirtytrigs) { dirtytrigs.SetSize(0); int cnt = 0; for (int j = 1; j <= chart.GetNChartT(); j++) { STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); for (int k = 1; k <= 3; k++) { STLTrigId nt = NeighbourTrig(t,k); if (GetChartNr(nt) != chartnum && outercharttrigs[nt] != chartnum) { STLPointId np1, np2; tt.GetNeighbourPoints(GetTriangle(nt),np1,np2); if (!IsEdge(np1,np2)) { dirtytrigs.Append(j); //local numbers!!! cnt++; break; //only once per trig!!! } } } } cnt = 0; STLPointId ap1, ap2, pn; Array trigsaroundp; for (int j = chart.GetNChartT(); j >= 1; j--) { STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); for (int k = 1; k <= 3; k++) { pn = tt.PNum(k); //if (chartpointchecked.Get(pn) == chartnum) //{continue;} int checkpoint = 0; for (int n = 1; n <= trigsperpoint.EntrySize(pn); n++) { if (trigsperpoint.Get(pn,n) != t && //ueberfluessig??? GetChartNr(trigsperpoint.Get(pn,n)) != chartnum && outercharttrigs[trigsperpoint.Get(pn,n)] != chartnum) {checkpoint = 1;}; } if (checkpoint) { chartpointchecked.Elem(pn) = chartnum; GetSortedTrianglesAroundPoint(pn,t,trigsaroundp); trigsaroundp.Append(t); //ring bool problem = false; //forward: for (int l = 2; l <= trigsaroundp.Size()-1; l++) { STLTrigId tn1 = trigsaroundp[l-2]; STLTrigId tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); if (IsEdge(ap1,ap2)) break; if (GetChartNr(tn2) != chartnum && outercharttrigs[tn2] != chartnum) {problem = true;} } //backwards: for (int l = trigsaroundp.Size()-1; l >= 2; l--) { STLTrigId tn1 = trigsaroundp[l]; STLTrigId tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); if (IsEdge(ap1,ap2)) break; if (GetChartNr(tn2) != chartnum && outercharttrigs[tn2] != chartnum) {problem = true;} } // if (problem && !IsInArray(j,dirtytrigs)) if (problem && !dirtytrigs.Contains(j)) { dirtytrigs.Append(j); cnt++; break; //only once per triangle } } } } } }