netgen/libsrc/stlgeom/stlgeomchart.cpp
2022-08-09 17:40:21 +02:00

845 lines
24 KiB
C++

//20.11.1999 third part of stlgeom.cc, functions with chart and atlas
#include <mystdlib.h>
#include <myadt.hpp>
#include <linalg.hpp>
#include <gprim.hpp>
#include <meshing.hpp>
#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<ChartId,STLTrigId> outermark(GetNT()); //marks all trigs form actual outer region
Array<ChartId,STLTrigId> outertested(GetNT()); //marks tested trigs for outer region
Array<ChartId,STLPointId> pointstochart(GetNP()); //point in chart becomes chartnum
Array<ChartId,STLPointId> innerpointstochart(GetNP()); //point in chart becomes chartnum
Array<STLPointId> chartpoints; //point in chart becomes chartnum
Array<STLPointId> innerchartpoints;
Array<Point<3>> innerchartpts;
NgArray<int> dirtycharttrigs;
NgArray<int> chartdistacttrigs (GetNT()); //outercharttrigs
chartdistacttrigs = 0;
STLBoundary chartbound(this); //knows the actual chart boundary
//int chartboundarydivisions = 10;
markedsegs.SetSize(0); //for testing!!!
NgArray<ChartId> 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<STLChart> (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);
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 <= NONeighbourTrigs(nt); 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]);
// 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 <= NONeighbourTrigs(nt); 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");
for (auto & chart : atlas)
chart->BuildInnerSearchTree();
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<int>& 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<twoint>& 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 <= NONeighbourTrigs(t); 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<ChartId,STLTrigId>& outercharttrigs,
NgArray<ChartId>& chartpointchecked,
NgArray<int>& 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 <= NONeighbourTrigs(t); 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<STLTrigId> 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
}
}
}
}
}
}