#include <meshing.hpp>
#include <core/register_archive.hpp>

#include "stlgeom.hpp"

namespace netgen
{

//globalen searchtree fuer gesamte geometry aktivieren
int geomsearchtreeon = 0;

int usechartnormal = 1;  

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void STLMeshing (STLGeometry & geom,
		 Mesh & mesh,
                 const MeshingParameters& mparam,
                 const STLParameters& stlpar)
{
  geom.Clear();
  geom.BuildEdges(stlpar);
  geom.MakeAtlas(mesh, mparam, stlpar);
  if (multithread.terminate) { return; }
  geom.CalcFaceNums();
  geom.AddFaceEdges();
  geom.LinkEdges(stlpar);

  mesh.ClearFaceDescriptors();
  for (int i = 1; i <= geom.GetNOFaces(); i++)
    mesh.AddFaceDescriptor (FaceDescriptor (i, 1, 0, 0));
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++   STL GEOMETRY   ++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


  STLGeometry :: STLGeometry()
  /*
  : edges(), edgesperpoint(),
    normals(),  externaledges(),
    atlas(), chartmark(), 
    lines(), outerchartspertrig(), vicinity(), markedtrigs(), markedsegs(),
    lineendpoints(), spiralpoints(), selectedmultiedge()
  */
{
  edgedata = make_unique<STLEdgeDataList>(*this);
  externaledges.SetSize(0);
  Clear();
  meshchart = 0; // initialize all ?? JS

  if (geomsearchtreeon)
    searchtree = new BoxTree<3> (GetBoundingBox().PMin() - Vec3d(1,1,1),
                                 GetBoundingBox().PMax() + Vec3d(1,1,1));
  else
    searchtree = NULL;

  status = STL_GOOD;
  statustext = "Good Geometry";
  smoothedges = NULL;
  area = -1;
}

STLGeometry :: ~STLGeometry()
{
  // for (auto p : atlas) delete p;
  // delete edgedata;
}

void STLGeometry :: Save (const filesystem::path & filename) const
{
  string ext = ToLower(filename.extension());

  if (ext == ".stl")
    {
      STLTopology::Save (filename);
      return;
    }
  else if (ext == ".stlb")
    {
      SaveBinary (filename,"Binary STL Geometry");
      return;
    }
  else if (ext == ".stle")
    {
      SaveSTLE (filename);
      return;
    }

  throw Exception ("Unknown target format: " + filename.string());
}



DLL_HEADER extern STLParameters stlparam;
int STLGeometry :: GenerateMesh (shared_ptr<Mesh> & mesh, MeshingParameters & mparam)
{
  STLParameters stlpar = stlparam;
  return STLMeshingDummy (this, mesh, mparam, stlpar);
}

Vec<3> STLGeometry :: GetNormal(int surfind, const Point<3> & p, const PointGeomInfo* gi) const
{
  if(!gi)
    throw Exception("STLGeometry::GetNormal without PointGeomInfo called");
  return GetChart(GetChartNr(gi->trignum)).GetNormal();
}

bool STLGeometry :: CalcPointGeomInfo(int /*surfind*/, PointGeomInfo& gi, const Point<3> & p3) const
{
  Point<3> hp = p3;
  SelectChartOfTriangle(gi.trignum);

  gi.trignum = Project (hp);

  if (gi.trignum) return true;

  return false;
}

bool STLGeometry :: ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const
{
  static std::mutex mutex_project_whole_surface;
  int meshchart = GetChartNr(gi.trignum);
  const STLChart& chart = GetChart(meshchart);
  int trignum = chart.ProjectNormal(p);
  if(trignum==0)
    {
      // non-thread-safe implementation
      std::lock_guard<std::mutex> guard(mutex_project_whole_surface);
      PrintMessage(7,"project failed");
      SelectChartOfTriangle (gi.trignum); // needed because ProjectOnWholeSurface uses meshchartnv (the normal vector of selected chart)
      trignum = ProjectOnWholeSurface(p);
      if(trignum==0)
        {
	  PrintMessage(7, "project on whole surface failed");
          return false;
        }
    }
  return true;
}

PointGeomInfo STLGeometry :: ProjectPoint (INDEX surfind, Point<3> & p) const
{
  throw Exception("ProjectPoint without PointGeomInfo not implemented");
}

void STLGeometry ::
PointBetween  (const Point<3> & p1, const Point<3> & p2, double secpoint,
	       int surfi,
	       const PointGeomInfo & gi1,
	       const PointGeomInfo & gi2,
	       Point<3> & newp, PointGeomInfo & newgi) const
{
  newp = p1+secpoint*(p2-p1);

  /*
  (*testout) << "surf-between: p1 = " << p1 << ", p2 = " << p2
	     << ", gi = " << gi1 << " - " << gi2 << endl;
  */

  if (gi1.trignum > 0)
    {
      //      ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum);

      Point<3> np1 = newp;
      Point<3> np2 = newp;
      auto ngi1 = gi1;
      auto ngi2 = gi2;
      // SelectChartOfTriangle (gi1.trignum);
      int tn1 = ProjectPointGI (surfi, np1, ngi1);

      // SelectChartOfTriangle (gi2.trignum);
      int tn2 = ProjectPointGI (surfi, np2, ngi2);

      newgi.trignum = tn1; //urspruengliche version
      newp = np1;          //urspruengliche version

      if (!newgi.trignum)
	{ newgi.trignum = tn2; newp = np2; }
      if (!newgi.trignum) newgi.trignum = gi1.trignum;
    }
  else
    {
      //      (*testout) << "WARNING: PointBetween got geominfo = 0" << endl;
      newp =  p1+secpoint*(p2-p1);
      newgi.trignum = 0;
    }
}

void STLGeometry ::
PointBetweenEdge (const Point<3> & p1, const Point<3> & p2, double secpoint,
	      int surfi1, int surfi2,
	      const EdgePointGeomInfo & gi1,
	      const EdgePointGeomInfo & gi2,
	      Point<3> & newp, EdgePointGeomInfo & newgi) const
{
  /*
  (*testout) << "edge-between: p1 = " << p1 << ", p2 = " << p2
	     << ", gi1,2 = " << gi1 << ", " << gi2 << endl;
  */
  /*
  newp = Center (p1, p2);
  ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum);
  newgi.trignum = geom.Project (newp);
  */
  int hi;
  newgi.dist = (1.0-secpoint) * gi1.dist + secpoint*gi2.dist;
  newgi.edgenr = gi1.edgenr;

  /*
  (*testout) << "p1 = " << p1 << ", p2 = " << p2 << endl;
  (*testout) << "refedge: " << gi1.edgenr
	     << " d1 = " << gi1.dist << ", d2 = " << gi2.dist << endl;
  */
  newp = GetLine (gi1.edgenr)->GetPointInDist (GetPoints(), newgi.dist, hi);

  //  (*testout) << "newp = " << newp << endl;
}

void STLGeometry :: STLInfo(double* data)
{
  data[0] = GetNT();

  Box<3> b = GetBoundingBox();
  data[1] = b.PMin()(0);
  data[2] = b.PMax()(0);
  data[3] = b.PMin()(1);
  data[4] = b.PMax()(1);
  data[5] = b.PMin()(2);
  data[6] = b.PMax()(2);

  int i;
 
  int cons = 1;
  for (i = 1; i <= GetNT(); i++)
    {
      if (NONeighbourTrigs(i) != 3) {cons = 0;}
    }
  data[7] = cons;
}

void STLGeometry :: MarkNonSmoothNormals(const STLParameters& stlparam)
{

  PrintFnStart("Mark Non-Smooth Normals");

  int i,j;

  markedtrigs.SetSize(GetNT());

  for (i = 1; i <= GetNT(); i++)
    {
      SetMarkedTrig(i, 0);
    }

  double dirtyangle = stlparam.yangle/180.*M_PI;

  int cnt = 0;
  STLPointId lp1,lp2;
  for (i = 1; i <= GetNT(); i++)
    {
      for (j = 1; j <= NONeighbourTrigs(i); j++)
	{
	  if (GetAngle(i, NeighbourTrig(i,j)) > dirtyangle)
	    {
	      GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), lp1, lp2);
	      if (!IsEdge(lp1,lp2))
		{
		  if (!IsMarkedTrig(i)) {SetMarkedTrig(i,1); cnt++;}
		}
	    }
	}
    }

  PrintMessage(5,"marked ",cnt," non-smooth trig-normals");

}

void STLGeometry :: SmoothNormals(const STLParameters& stlparam)
{
  multithread.terminate = 0;

  //  UseExternalEdges();

  BuildEdges(stlparam);


  DenseMatrix m(3), hm(3);
  Vector rhs(3), sol(3), hv(3), hv2(3);

  Vec<3> ri;

  double wnb = stldoctor.smoothnormalsweight;   // neighbour normal weight
  double wgeom = 1-wnb;   // geometry normal weight


  // minimize 
  //  wgeom sum_T  \sum ri  \| ri^T (n - n_geom) \|^2  
  //  + wnb sum_SE  \| ri x (n - n_nb) \|^2
  
  int i, j, k, l;
  int nt = GetNT();
  
  PushStatusF("Smooth Normals");
    
  //int testmode;

  for (i = 1; i <= nt; i++)
    {

      SetThreadPercent( 100.0 * (double)i / (double)nt);

      const STLTriangle & trig = GetTriangle (i);
      
      m = 0;
      rhs = 0;

      // normal of geometry:
      Vec<3> ngeom = trig.GeomNormal(points);
      ngeom.Normalize();

      for (j = 1; j <= 3; j++)
	{ 
	  int pi1 = trig.PNumMod (j);
	  int pi2 = trig.PNumMod (j+1);

	  // edge vector
	  ri = GetPoint (pi2) - GetPoint (pi1);
	  
	  for (k = 0; k < 3; k++)
	    for (l = 0; l < 3; l++)
	      hm.Elem(k+1, l+1) = wgeom * ri(k) * ri(l);
	  
	  
	  for (k = 0; k < 3; k++)
	    hv(k) = ngeom(k);
	  
	  hm.Mult (hv, hv2);
	  /*
	  if (testmode)
	    (*testout) << "add vec " << hv2 << endl 
		       << " add m " << hm << endl;
	  */
	  rhs.Add (1, hv2);
	  m += hm;


	  int nbt = 0;
	  STLPointId fp1,fp2;
	  for (k = 1; k <= NONeighbourTrigs(i); k++)
	    {
	      trig.GetNeighbourPoints(GetTriangle(NeighbourTrig(i, k)),fp1,fp2);
	      if (fp1 == pi1 && fp2 == pi2)
		{
		  nbt = NeighbourTrig(i, k);
		}
	    }

	  if (!nbt)
	    {
	      cerr << "ERROR: stlgeom::Smoothnormals, nbt = 0" << endl;
	    }

	  // smoothed normal
	  Vec<3> nnb = GetTriangle(nbt).Normal();   // neighbour normal
	  nnb.Normalize();

	  if (!IsEdge(pi1,pi2)) 
	    {
	      double lr2 = ri * ri;
	      for (k = 0; k < 3; k++)
		{
		  for (l = 0; l < k; l++)
		    {
		      hm.Elem(k+1, l+1) = -wnb * ri(k) * ri(l);
		      hm.Elem(l+1, k+1) = -wnb * ri(k) * ri(l);
		    }
		  
		  hm.Elem(k+1, k+1) = wnb * (lr2 - ri(k) * ri(k));
		}
	      
	      for (k = 0; k < 3; k++)
		hv(k) = nnb(k);
	      
	      hm.Mult (hv, hv2);
	      /*
	      if (testmode)
		(*testout) << "add nb vec " << hv2 << endl 
			   << " add nb m " << hm << endl;
	      */

	      rhs.Add (1, hv2);
	      m += hm;
	    }
	}

      m.Solve (rhs, sol);
      Vec3d newn(sol(0), sol(1), sol(2));
      newn /= (newn.Length() + 1e-24);      

      GetTriangle(i).SetNormal(newn);
      // setnormal (sol);
    }

  /*
  for (i = 1; i <= nt; i++)
    SetMarkedTrig(i, 0);      		



  int crloop;
  for (crloop = 1; crloop <= 3; crloop++)
    {

  // find critical:

  NgArray<INDEX_2> critpairs;
  for (i = 1; i <= nt; i++)
    {
      const STLTriangle & trig = GetTriangle (i);
      
      Vec3d ngeom = GetTriangleNormal (i); // trig.Normal(points);
      ngeom /= (ngeom.Length() + 1e-24);

      for (j = 1; j <= 3; j++)
	{ 
	  int pi1 = trig.PNumMod (j);
	  int pi2 = trig.PNumMod (j+1);

	  int nbt = 0;
	  int fp1,fp2;
	  for (k = 1; k <= NONeighbourTrigs(i); k++)
	    {
	      trig.GetNeighbourPoints(GetTriangle(NeighbourTrig(i, k)),fp1,fp2);
	      if (fp1 == pi1 && fp2 == pi2)
		{
		  nbt = NeighbourTrig(i, k);
		}
	    }
	  
	  if (!nbt)
	    {
	      cerr << "ERROR: stlgeom::Smoothnormals, nbt = 0" << endl;
	    }

	  Vec3d nnb = GetTriangleNormal(nbt);   // neighbour normal
	  nnb /= (nnb.Length() + 1e-24);

	  if (!IsEdge(pi1,pi2)) 
	    {
	      if (Angle (nnb, ngeom) > 150 * M_PI/180)
		{
		  SetMarkedTrig(i, 1);      		
		  SetMarkedTrig(nbt, 1);      		
		  critpairs.Append (INDEX_2 (i, nbt));
		}
	    }

	}
    }

  if (!critpairs.Size())
    {
      break;
    }

  if (critpairs.Size())
    {

      NgArray<int> friends;
      double area1 = 0, area2 = 0;

      for (i = 1; i <= critpairs.Size(); i++)
	{
	  int tnr1 = critpairs.Get(i).I1();
	  int tnr2 = critpairs.Get(i).I2();
	  (*testout) << "t1 = " << tnr1 << ", t2 = " << tnr2
		     << " angle = " << Angle (GetTriangleNormal (tnr1),
					      GetTriangleNormal (tnr2))
		     << endl;

	  // who has more friends ?
	  int side;
	  area1 = 0;
	  area2 = 0;
	  for (side = 1; side <= 2; side++)
	    {
	      friends.SetSize (0);
	      friends.Append ( (side == 1) ? tnr1 : tnr2);

	      for (j = 1; j <= 3; j++)
		{
		  int fsize = friends.Size();
		  for (k = 1; k <= fsize; k++)
		    {
		      int testtnr = friends.Get(k);
		      Vec3d ntt = GetTriangleNormal(testtnr);
		      ntt /= (ntt.Length() + 1e-24);
		      
		      for (l = 1; l <= NONeighbourTrigs(testtnr); l++)
			{
			  int testnbnr = NeighbourTrig(testtnr, l);
			  Vec3d nbt = GetTriangleNormal(testnbnr);
			  nbt /= (nbt.Length() + 1e-24);

			  if (Angle (nbt, ntt) < 15 * M_PI/180)
			    {
			      int ii;
			      int found = 0;
			      for (ii = 1; ii <= friends.Size(); ii++)
				{
				  if (friends.Get(ii) == testnbnr)
				    {
				      found = 1;
				      break;
				    }
				}
			      if (!found)
				friends.Append (testnbnr);
			    }
			}
		    }
		}

	      // compute area:
	      for (k = 1; k <= friends.Size(); k++)
		{
		  double area = 
		    GetTriangle (friends.Get(k)).Area(points);

		  if (side == 1)
		    area1 += area;
		  else
		    area2 += area;
		}
	      
                }

	  (*testout) << "area1 = " << area1 << " area2 = " << area2 << endl;
	  if (area1 < 0.1 * area2)
	    {
	      Vec3d n = GetTriangleNormal (tnr1);
	      n *= -1;
	      SetTriangleNormal(tnr1, n);
	    }
	  if (area2 < 0.1 * area1)
	    {
	      Vec3d n = GetTriangleNormal (tnr2);
	      n *= -1;
	      SetTriangleNormal(tnr2, n);
	    }
	}
    }
    }
  */

  calcedgedataanglesnew = 1;
  PopStatus();
}


int STLGeometry :: AddEdge(int ap1, int ap2)
{
  STLEdge e(ap1,ap2);
  e.SetLeftTrig(GetLeftTrig(ap1,ap2));
  e.SetRightTrig(GetRightTrig(ap1,ap2));
  edges.Append(e);
  return edges.Size();
}

void STLGeometry :: STLDoctorConfirmEdge()
{
  StoreEdgeData();
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
    {
      if (stldoctor.selectmode == 1)
	{
	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CONFIRMED);
	}
      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
	{
	  int i;
	  for (i = 1; i <= selectedmultiedge.Size(); i++)
	    {
	      int ap1 = selectedmultiedge.Get(i).i1;
	      int ap2 = selectedmultiedge.Get(i).i2;
	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CONFIRMED);
	    }
	}
    }
}

void STLGeometry :: STLDoctorCandidateEdge()
{
  StoreEdgeData();
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
    {
      if (stldoctor.selectmode == 1)
	{
	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CANDIDATE);
	}
      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
	{
	  int i;
	  for (i = 1; i <= selectedmultiedge.Size(); i++)
	    {
	      int ap1 = selectedmultiedge.Get(i).i1;
	      int ap2 = selectedmultiedge.Get(i).i2;
	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CANDIDATE);
	    }
	}
    }
}

void STLGeometry :: STLDoctorExcludeEdge()
{
  StoreEdgeData();
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
    {
      if (stldoctor.selectmode == 1)
	{
	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_EXCLUDED);
	}
      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
	{
	  int i;
	  for (i = 1; i <= selectedmultiedge.Size(); i++)
	    {
	      int ap1 = selectedmultiedge.Get(i).i1;
	      int ap2 = selectedmultiedge.Get(i).i2;
	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_EXCLUDED);
	    }
	}
    }
}

void STLGeometry :: STLDoctorUndefinedEdge()
{
  StoreEdgeData();
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
    {
      if (stldoctor.selectmode == 1)
	{
	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_UNDEFINED);
	}
      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
	{
	  int i;
	  for (i = 1; i <= selectedmultiedge.Size(); i++)
	    {
	      int ap1 = selectedmultiedge.Get(i).i1;
	      int ap2 = selectedmultiedge.Get(i).i2;
	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_UNDEFINED);
	    }
	}
    }
}

void STLGeometry :: STLDoctorSetAllUndefinedEdges()
{
  edgedata->ResetAll();
}

void STLGeometry :: STLDoctorEraseCandidateEdges()
{
  StoreEdgeData();
  edgedata->ChangeStatus(ED_CANDIDATE, ED_UNDEFINED);
}

void STLGeometry :: STLDoctorConfirmCandidateEdges()
{
  StoreEdgeData();
  edgedata->ChangeStatus(ED_CANDIDATE, ED_CONFIRMED);
}

void STLGeometry :: STLDoctorConfirmedToCandidateEdges()
{
  StoreEdgeData();
  edgedata->ChangeStatus(ED_CONFIRMED, ED_CANDIDATE);
}

void STLGeometry :: STLDoctorDirtyEdgesToCandidates()
{
  StoreEdgeData();
}

void STLGeometry :: STLDoctorLongLinesToCandidates()
{
  StoreEdgeData();
}

twoint STLGeometry :: GetNearestSelectedDefinedEdge()
{
  Point<3> pestimate = Center(GetTriangle(GetSelectTrig()).center,
  			     GetPoint(GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig())));
    //Point3d pestimate = GetTriangle(GetSelectTrig()).center;

  int i, j, en;
  NgArray<int> vic;
  GetVicinity(GetSelectTrig(),4,vic);
  

  twoint fedg;
  fedg.i1 = 0;
  fedg.i2 = 0;
  double mindist = 1E50;
  double dist;
  Point<3> p;

  for (i = 1; i <= vic.Size(); i++)
  {
    const STLTriangle& t = GetTriangle(vic.Get(i));
    for (j = 1; j <= 3; j++)
      {
	en = edgedata->GetEdgeNum(t.PNum(j),t.PNumMod(j+1));
	if (edgedata->Get(en).GetStatus() != ED_UNDEFINED)
	  {
	    p = pestimate;
	    dist = GetDistFromLine(GetPoint(t.PNum(j)),GetPoint(t.PNumMod(j+1)),p);
	    if (dist < mindist)
	      {
		mindist = dist;
		fedg.i1 = t.PNum(j);
		fedg.i2 = t.PNumMod(j+1);
	      }
	  }
      }
  }
  return fedg;
}
 
void STLGeometry :: BuildSelectedMultiEdge(twoint ep)
{
  if (edgedata->Size() == 0 || 
      !GetEPPSize()) 
    {
      return; 
    }

  selectedmultiedge.SetSize(0);
  int tenum = GetTopEdgeNum (ep.i1, ep.i2);

  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
    {
      twoint epnew = GetNearestSelectedDefinedEdge();
      if (epnew.i1) 
	{
	  ep = epnew;
	  tenum = GetTopEdgeNum (ep.i1, ep.i2);
	}
    }

  selectedmultiedge.Append(twoint(ep));

  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
    {
      return;
    }

  edgedata->BuildLineWithEdge(ep.i1,ep.i2,selectedmultiedge);
}

void STLGeometry :: BuildSelectedEdge(twoint ep)
{
  if (edgedata->Size() == 0 || 
      !GetEPPSize()) 
    {
      return; 
    }

  selectedmultiedge.SetSize(0);

  selectedmultiedge.Append(twoint(ep));
}

void STLGeometry :: BuildSelectedCluster(twoint ep)
{
  if (edgedata->Size() == 0 || 
      !GetEPPSize()) 
    {
      return; 
    }

  selectedmultiedge.SetSize(0);

  int tenum = GetTopEdgeNum (ep.i1, ep.i2);

  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
    {
      twoint epnew = GetNearestSelectedDefinedEdge();
      if (epnew.i1) 
	{
	  ep = epnew;
	  tenum = GetTopEdgeNum (ep.i1, ep.i2);
	}
    }

  selectedmultiedge.Append(twoint(ep));

  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
    {
      return;
    }

  edgedata->BuildClusterWithEdge(ep.i1,ep.i2,selectedmultiedge);
}

void STLGeometry :: ImportEdges()
{
  StoreEdgeData();

  PrintMessage(5, "import edges from file 'edges.ng'");
  ifstream fin("edges.ng");

  int ne;
  fin >> ne;

  NgArray<Point<3> > eps;

  int i;
  Point<3> p;
  for (i = 1; i <= 2*ne; i++)
    {
      fin >> p(0); 
      fin >> p(1); 
      fin >> p(2);
      eps.Append(p);
    }
  AddEdges(eps);
}

void STLGeometry :: AddEdges(const NgArray<Point<3> >& eps)
{
  int i;
  int ne = eps.Size()/2;
  
  NgArray<int> epsi;
  Box<3> bb = GetBoundingBox();
  bb.Increase(1);

  Point3dTree ptree (bb.PMin(), 
			 bb.PMax());
  NgArray<int> pintersect;

  double gtol = GetBoundingBox().Diam()/1.E10;
  Point<3> p;

  for (i = 1; i <= GetNP(); i++)
    {
      p = GetPoint(i);
      ptree.Insert (p, i);
    }
  
  int error = 0;
  for (i = 1; i <= 2*ne; i++)
    {
      p = eps.Get(i);
      Point3d pmin = p - Vec3d (gtol, gtol, gtol);
      Point3d pmax = p + Vec3d (gtol, gtol, gtol);
	  
      ptree.GetIntersecting (pmin, pmax, pintersect);
      if (pintersect.Size() > 1)
	{
	  PrintError("Found too much points in epsilon-dist");
	  error = 1;
	}
      else if (pintersect.Size() == 0)
	{
	  error = 1;
	  PrintError("edgepoint does not exist!");
	  PrintMessage(5,"p=",Point3d(eps.Get(i)));
	}
      else
	{
	  epsi.Append(pintersect.Get(1));
	}
    }

  if (error) return;

  int en;
  for (i = 1; i <= ne; i++)
    {
      if (epsi.Get(2*i-1) == epsi.Get(2*i)) {PrintError("Edge with zero length!");}
      else 
	{
	  en = edgedata->GetEdgeNum(epsi.Get(2*i-1),epsi.Get(2*i));
	  edgedata->Elem(en).SetStatus (ED_CONFIRMED);
	}
    }

}



void STLGeometry :: ImportExternalEdges(const char * filename)
{
  //AVL edges!!!!!!

  ifstream inf (filename);
  char ch;
  //int cnt = 0;
  int records, units, i, j;
  PrintFnStart("Import edges from ",filename);
  
  const int flen=30;
  char filter[flen+1];
  filter[flen] = 0;
  char buf[20];

  NgArray<Point3d> importpoints;
  NgArray<int> importlines;
  NgArray<int> importpnums;

  while (inf.good())
    {
      inf.get(ch);
      //      (*testout) << cnt << ": " << ch << endl;
      
      for (i = 0; i < flen; i++)
	filter[i] = filter[i+1];
      filter[flen-1] = ch;
      //      (*testout) << filter << endl;

      if (strcmp (filter+flen-7, "RECORDS") == 0)
	{
	  inf.get(ch);  // '='
	  inf >> records;
	}
      if (strcmp (filter+flen-5, "UNITS") == 0)
	{
	  inf.get(ch);  // '='
	  inf >> units;
	}

      if (strcmp (filter+flen-17, "EDGE NODE NUMBERS") == 0)
	{
	  int nodenr;
	  importlines.SetSize (units);
	  for (i = 1; i <= units; i++)
	    {
	      inf >> nodenr;
	      importlines.Elem(i) = nodenr;
	      //	      (*testout) << nodenr << endl;
	    }
	}

      if (strcmp (filter+flen-23, "EDGE POINT COORD IN DIR") == 0)
	{
	  int coord;

	  inf >> coord;
	  
	  importpoints.SetSize (units);

	  inf >> ch;
	  inf.putback (ch);

	  for (i = 1; i <= units; i++)
	    {
	      for (j = 0; j < 12; j++)
		inf.get (buf[j]);
	      buf[12] = 0;

	      importpoints.Elem(i).X(coord) = 1000 * atof (buf);
	    }
	}
    }

  /*
  (*testout) << "lines: " << endl;
  for (i = 1; i <= importlines.Size(); i++)
    (*testout) << importlines.Get(i) << endl;
  (*testout) << "points: " << endl;
  for (i = 1; i <= importpoints.Size(); i++)
    (*testout) << importpoints.Get(i) << endl;
  */



  importpnums.SetSize (importpoints.Size());
  

  Box3d bb (GetBoundingBox().PMin() + Vec3d (-1,-1,-1),
	    GetBoundingBox().PMax() + Vec3d (1, 1, 1));

  Point3dTree ptree (bb.PMin(), 
			 bb.PMax());


  PrintMessage(7,"stl - bb: ",bb.PMin(), " - ", bb.PMax());
  
  Box3d ebb;
  ebb.SetPoint (importpoints.Get(1));
  for (i = 1; i <= importpoints.Size(); i++)
    ebb.AddPoint (importpoints.Get(i));
  PrintMessage(7,"edgep - bb: ", ebb.PMin(), " - ", ebb.PMax());

  NgArray<int> pintersect;

  double gtol = GetBoundingBox().Diam()/1.E6;

  for (i = 1; i <= GetNP(); i++)
    {
      Point3d p = GetPoint(i);
      //      (*testout) << "stlpt: " << p << endl;
      ptree.Insert (p, i);
    }
  

  for (i = 1; i <= importpoints.Size(); i++)
    {
      Point3d p = importpoints.Get(i);
      Point3d pmin = p - Vec3d (gtol, gtol, gtol);
      Point3d pmax = p + Vec3d (gtol, gtol, gtol);
	  
      ptree.GetIntersecting (pmin, pmax, pintersect);
      if (pintersect.Size() > 1)
	{
	  importpnums.Elem(i) = 0;
	  PrintError("Found too many points in epsilon-dist");
	}
      else if (pintersect.Size() == 0)
	{
	  importpnums.Elem(i) = 0;
	  PrintError("Edgepoint does not exist!");
	}
      else
	{
	  importpnums.Elem(i) = pintersect.Get(1);
	}
    }

  //  if (!error) 
    {
      PrintMessage(7,"found all edge points in stl file");


      StoreEdgeData();

      int oldp = 0;

      for (i = 1; i <= importlines.Size(); i++)
	{
	  int newp = importlines.Get(i);
	  if (!importpnums.Get(abs(newp)))
	    newp = 0;

	  if (oldp && newp)
	    {
	      int en = edgedata->GetEdgeNum(importpnums.Get(oldp), 
					   importpnums.Get(abs(newp)));
	      edgedata->Elem(en).SetStatus (ED_CONFIRMED);
	    }
	  
	  if (newp < 0)
	    oldp = 0;
	  else
	    oldp = newp;
	}
    }


}



void STLGeometry :: ExportEdges()
{
  PrintFnStart("Save edges to file 'edges.ng'");

  ofstream fout("edges.ng");
  fout.precision(16);

  int n = edgedata->GetNConfEdges();
  
  fout << n << endl;

  int i;
  for (i = 1; i <= edgedata->Size(); i++)
    {
      if (edgedata->Get(i).GetStatus() == ED_CONFIRMED)
	{
	  const STLTopEdge & e = edgedata->Get(i);
	  fout << GetPoint(e.PNum(1))(0) << " " << GetPoint(e.PNum(1))(1) << " " << GetPoint(e.PNum(1))(2) << endl;
	  fout << GetPoint(e.PNum(2))(0) << " " << GetPoint(e.PNum(2))(1) << " " << GetPoint(e.PNum(2))(2) << endl;
	}
    }

}

void STLGeometry :: LoadEdgeData(const filesystem::path & filename)
{
  StoreEdgeData();

  PrintFnStart("Load edges from file '", filename, "'");
  ifstream fin(filename);

  edgedata->Read(fin);

  //  calcedgedataanglesnew = 1;
}

void STLGeometry :: SaveEdgeData(const filesystem::path & filename)
{
  PrintFnStart("save edges to file '", filename, "'");
  ofstream fout(filename);

  edgedata->Write(fout);
}







/*
void STLGeometry :: SaveExternalEdges()
{
  ofstream fout("externaledgesp3.ng");
  fout.precision(16);

  int n = NOExternalEdges();
  fout << n << endl;

  int i;
  for (i = 1; i <= n; i++)
    {
      twoint e = GetExternalEdge(i);
      fout << GetPoint(e.i1)(0) << " " << GetPoint(e.i1)(1) << " " << GetPoint(e.i1)(2) << endl;
      fout << GetPoint(e.i2)(0) << " " << GetPoint(e.i2)(1) << " " << GetPoint(e.i2)(2) << endl;
    }

}
*/
void STLGeometry :: StoreExternalEdges()
{
  storedexternaledges.SetSize(0);
  undoexternaledges = 1;
  int i;
  for (i = 1; i <= externaledges.Size(); i++)
    {
      storedexternaledges.Append(externaledges.Get(i));      
    }

}

void STLGeometry :: UndoExternalEdges()
{
  if (!undoexternaledges) 
    {
      PrintMessage(1, "undo not further possible!");
      return;
    }
  RestoreExternalEdges();
  undoexternaledges = 0;
}

void STLGeometry :: RestoreExternalEdges()
{
  externaledges.SetSize(0);
  int i;
  for (i = 1; i <= storedexternaledges.Size(); i++)
    {
      externaledges.Append(storedexternaledges.Get(i));      
    }

}


void STLGeometry :: AddExternalEdgeAtSelected()
{
  StoreExternalEdges();
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
    {
      int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
      int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}
    }
}

void STLGeometry :: AddClosedLinesToExternalEdges()
{
  StoreExternalEdges();

  int i, j;
  for (i = 1; i <= GetNLines(); i++)
    {
      STLLine* l = GetLine(i);
      if (l->StartP() == l->EndP()) 
	{
	  for (j = 1; j < l->NP(); j++)
	    {
	      int ap1 = l->PNum(j);
	      int ap2 = l->PNum(j+1);

	      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}	      
	    }
	}
    }
}

void STLGeometry :: AddLongLinesToExternalEdges()
{
  StoreExternalEdges();

  double diamfact = stldoctor.dirtytrigfact;
  double diam = GetBoundingBox().Diam();

  int i, j;
  for (i = 1; i <= GetNLines(); i++)
    {
      STLLine* l = GetLine(i);
      if (l->GetLength(points) >= diamfact*diam) 
	{
	  for (j = 1; j < l->NP(); j++)
	    {
	      int ap1 = l->PNum(j);
	      int ap2 = l->PNum(j+1);

	      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}	      
	    }
	}
    }
}

void STLGeometry :: AddAllNotSingleLinesToExternalEdges()
{
  StoreExternalEdges();

  int i, j;
  for (i = 1; i <= GetNLines(); i++)
    {
      STLLine* l = GetLine(i);
      if (GetNEPP(l->StartP()) > 1 || GetNEPP(l->EndP()) > 1) 
	{
	  for (j = 1; j < l->NP(); j++)
	    {
	      int ap1 = l->PNum(j);
	      int ap2 = l->PNum(j+1);

	      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}	      
	    }
	}
    }
}

void STLGeometry :: DeleteDirtyExternalEdges()
{
  //delete single triangle edges and single edge-lines in clusters"
  StoreExternalEdges();

  int i, j;
  for (i = 1; i <= GetNLines(); i++)
    {
      STLLine* l = GetLine(i);
      if (l->NP() <= 3 || (l->StartP() == l->EndP() && l->NP() == 4))
	{
	  for (j = 1; j < l->NP(); j++)
	    {
	      int ap1 = l->PNum(j);
	      int ap2 = l->PNum(j+1);

	      if (IsExternalEdge(ap1,ap2)) {DeleteExternalEdge(ap1,ap2);}	      
	    }
	}
    }
}

void STLGeometry :: AddExternalEdgesFromGeomLine()
{
  StoreExternalEdges();
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
    {
      int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
      int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);

      if (IsEdge(ap1,ap2))
	{
	  int edgenum = IsEdgeNum(ap1,ap2);
	  if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}
	  
	  int noend = 1;
	  int startp = ap1;
	  int laste = edgenum;
	  int np1, np2;
	  while (noend)
	    {
	      if (GetNEPP(startp) == 2)
		{
		  if (GetEdgePP(startp,1) != laste) {laste = GetEdgePP(startp,1);}
		  else {laste = GetEdgePP(startp,2);}
		  np1 = GetEdge(laste).PNum(1);
		  np2 = GetEdge(laste).PNum(2);
		  
		  if (!IsExternalEdge(np1, np2)) {AddExternalEdge(np1, np2);}
		  else {noend = 0;}
		  if (np1 != startp) {startp = np1;}
		  else {startp = np2;}
		}
	      else {noend = 0;}
	    }

	  startp = ap2;
	  laste = edgenum;
	  noend = 1;
	  while (noend)
	    {
	      if (GetNEPP(startp) == 2)
		{
		  if (GetEdgePP(startp,1) != laste) {laste = GetEdgePP(startp,1);}
		  else {laste = GetEdgePP(startp,2);}
		  np1 = GetEdge(laste).PNum(1);
		  np2 = GetEdge(laste).PNum(2);
		  
		  if (!IsExternalEdge(np1, np2)) {AddExternalEdge(np1, np2);}
		  else {noend = 0;}
		  if (np1 != startp) {startp = np1;}
		  else {startp = np2;}
		}
	      else {noend = 0;}
	    }
	  
	}

    }
  
}

void STLGeometry :: ClearEdges()
{
  edgesfound = 0;
  edges.SetSize(0);
  //edgedata->SetSize(0);
  // externaledges.SetSize(0);
  edgesperpoint.SetSize(0);
  undoexternaledges = 0;

}

void STLGeometry :: STLDoctorBuildEdges(const STLParameters& stlparam)
{
  //  if (!trigsconverted) {return;}
  ClearEdges();

  meshlines.SetSize(0);
  FindEdgesFromAngles(stlparam);
}

void STLGeometry :: DeleteExternalEdgeAtSelected()
{
  StoreExternalEdges();
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
    {
      int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
      int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
      if (IsExternalEdge(ap1,ap2)) {DeleteExternalEdge(ap1,ap2);}
    }
}

void STLGeometry :: DeleteExternalEdgeInVicinity()
{
  StoreExternalEdges();
  if (!stldoctor.showvicinity || vicinity.Size() != GetNT()) {return;}

  int i, j, ap1, ap2;
  
  for (i = 1; i <= GetNT(); i++)
    {
      if (vicinity.Elem(i))
	{
	  for (j = 1; j <= 3; j++)
	    {
	      ap1 = GetTriangle(i).PNum(j);
	      ap2 = GetTriangle(i).PNumMod(j+1);

	      if (IsExternalEdge(ap1,ap2))
		{
		  DeleteExternalEdge(ap1,ap2);
		}
	    }
	}
    }
}

void STLGeometry :: BuildExternalEdgesFromEdges()
{
  StoreExternalEdges();

  if (GetNE() == 0) {PrintWarning("Edges possibly not generated!");}

  int i;
  externaledges.SetSize(0);

  for (i = 1; i <= GetNE(); i++)
    {
      STLEdge e = GetEdge(i);
      AddExternalEdge(e.PNum(1), e.PNum(2));
    }

}


void STLGeometry :: AddExternalEdge(int ap1, int ap2)
{
  externaledges.Append(twoint(ap1,ap2));
}

void STLGeometry :: DeleteExternalEdge(int ap1, int ap2)
{

  int i;
  int found = 0;
  for (i = 1; i <= NOExternalEdges(); i++)
    {
      if ((GetExternalEdge(i).i1 == ap1 && GetExternalEdge(i).i2 == ap2) ||
	  (GetExternalEdge(i).i1 == ap2 && GetExternalEdge(i).i2 == ap1)) {found = 1;};
      if (found && i < NOExternalEdges())
	{
	  externaledges.Elem(i) = externaledges.Get(i+1);
	}
    }
  if (!found) {PrintWarning("edge not found");}
  else
    {
      externaledges.SetSize(externaledges.Size()-1);
    }

}

int STLGeometry :: IsExternalEdge(int ap1, int ap2)
{
  int i;
  for (i = 1; i <= NOExternalEdges(); i++)
    {
      if ((GetExternalEdge(i).i1 == ap1 && GetExternalEdge(i).i2 == ap2) ||
	  (GetExternalEdge(i).i1 == ap2 && GetExternalEdge(i).i2 == ap1)) {return 1;};
    }
  return 0;
}

void STLGeometry :: DestroyDirtyTrigs()
{

  PrintFnStart("Destroy dirty triangles");
  PrintMessage(5,"original number of triangles=", GetNT());

  //destroy every triangle with other than 3 neighbours;
  int changed = 1;
  int i, j, k;
  while (changed)
    {
      changed = 0;
      Clear();

      for (i = 1; i <= GetNT(); i++)
	{
	  int dirty = NONeighbourTrigs(i) < 3;

	  for (j = 1; j <= 3; j++)
	    {
	      int pnum = GetTriangle(i).PNum(j);
	      /*
	      if (pnum == 1546)
		{
		// for (k = 1; k <=  NOTrigsPerPoint(pnum); k++)
		}
	      */
	      if (NOTrigsPerPoint(pnum) <= 2) 
		dirty = 1;
	    }
	  
	  int pi1 = GetTriangle(i).PNum(1);
	  int pi2 = GetTriangle(i).PNum(2);
	  int pi3 = GetTriangle(i).PNum(3);
	  if (pi1 == pi2 || pi1 == pi3 || pi2 == pi3)
	    {
	      PrintMessage(5,"triangle with Volume 0: ", i, "  nodes: ", pi1, ", ", pi2, ", ", pi3);
	      dirty = 1;
	    }

	  if (dirty)
	    {
	      for (k = i+1; k <= GetNT(); k++)
		{
		  trias[k-1] = trias[k];
		  // readtrias: not longer permanent, JS
		  //		  readtrias.Elem(k-1) = readtrias.Get(k); 
		}
	      int size = GetNT();
	      trias.SetSize(size-1);
	      //	      readtrias.SetSize(size-1);
	      changed = 1;
	      break;
	    }
	}
    }  

  FindNeighbourTrigs();
  PrintMessage(5,"final number of triangles=", GetNT());
}

void STLGeometry :: CalcNormalsFromGeometry()
{
  int i;
  for (i = 1; i <= GetNT(); i++)
    {
      const STLTriangle & tr = GetTriangle(i);
      const Point3d& ap1 = GetPoint(tr.PNum(1));
      const Point3d& ap2 = GetPoint(tr.PNum(2));
      const Point3d& ap3 = GetPoint(tr.PNum(3));

      Vec3d normal = Cross (ap2-ap1, ap3-ap1);
      
      if (normal.Length() != 0)
	{
	  normal /= (normal.Length());		  
	}
      GetTriangle(i).SetNormal(normal);
    }
  PrintMessage(5,"Normals calculated from geometry!!!");

  calcedgedataanglesnew = 1;
}

void STLGeometry :: SetSelectTrig(int trig)
{
  stldoctor.selecttrig = trig;
}

int STLGeometry :: GetSelectTrig() const
{
  return stldoctor.selecttrig;
}

void STLGeometry :: SetNodeOfSelTrig(int n)
{
  stldoctor.nodeofseltrig = n;
}

int STLGeometry :: GetNodeOfSelTrig() const
{
  return stldoctor.nodeofseltrig;
}

void STLGeometry :: MoveSelectedPointToMiddle()
{
  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
    {
      int p = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
      Point<3> pm(0.,0.,0.); //Middlevector;
      Point<3> p0(0.,0.,0.);
      PrintMessage(5,"original point=", Point3d(GetPoint(p)));

      int i;
      int cnt = 0;
      for (i = 1; i <= trigsperpoint.EntrySize(p); i++)
	{
	  const STLTriangle& tr = GetTriangle(trigsperpoint.Get(p,i));
	  int j;
	  for (j = 1; j <= 3; j++)
	    {
	      if (tr.PNum(j) != p)
		{
		  cnt++;
		  pm(0) += GetPoint(tr.PNum(j))(0);
		  pm(1) += GetPoint(tr.PNum(j))(1);
		  pm(2) += GetPoint(tr.PNum(j))(2);
		}
	    }
	}

      Point<3> origp = GetPoint(p);
      double fact = 0.2;

      SetPoint(p, p0 + fact*(1./(double)cnt)*(pm-p0)+(1.-fact)*(origp-p0));

      PrintMessage(5,"middle point=", Point3d (GetPoint(p)));
      
      PrintMessage(5,"moved point ", Point3d (p));

    }
}

void STLGeometry :: PrintSelectInfo()
{

  //int trig = GetSelectTrig();
  //int p = GetTriangle(trig).PNum(GetNodeOfSelTrig());
  
  PrintMessage(1,"touch triangle ", GetSelectTrig()
               , ", local node ", GetNodeOfSelTrig()
               , " (=", int(GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig())), ")");
  if (AtlasMade() && GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
    {
      PrintMessage(1,"           chartnum=", int(GetChartNr(GetSelectTrig())));
      /*      
      PointBetween(Center(Center(GetPoint(GetTriangle(270).PNum(1)),
				 GetPoint(GetTriangle(270).PNum(2))),
			  GetPoint(GetTriangle(270).PNum(3))),270,
		   Center(Center(GetPoint(GetTriangle(trig).PNum(1)),
				 GetPoint(GetTriangle(trig).PNum(2))),
			  GetPoint(GetTriangle(trig).PNum(3))),trig);
      */
      //PointBetween(Point3d(5.7818, 7.52768, 4.14879),260,Point3d(6.80292, 6.55392, 4.70184),233);
    }
}

void STLGeometry :: ShowSelectedTrigChartnum()
{
  int st = GetSelectTrig();

  if (st >= 1 && st <= GetNT() && AtlasMade())
    PrintMessage(1,"selected trig ", st, " has chartnumber ", int(GetChartNr(st)));
}

void STLGeometry :: ShowSelectedTrigCoords()
{
  int st = GetSelectTrig();

  /*
  //testing!!!!
  NgArray<int> trigs;
  GetSortedTrianglesAroundPoint(GetTriangle(st).PNum(GetNodeOfSelTrig()),st,trigs);
  */

  if (st >= 1 && st <= GetNT())
    {
      PrintMessage(1, "coordinates of selected trig ", st, ":");
      PrintMessage(1, "   p1 = ", int(GetTriangle(st).PNum(1)), " = ", 
		   Point3d (GetPoint(GetTriangle(st).PNum(1))));
      PrintMessage(1, "   p2 = ", int(GetTriangle(st).PNum(2)), " = ", 
		   Point3d (GetPoint(GetTriangle(st).PNum(2))));
      PrintMessage(1, "   p3 = ", int(GetTriangle(st).PNum(3)), " = ", 
		   Point3d (GetPoint(GetTriangle(st).PNum(3))));
    }
}

void STLGeometry :: LoadMarkedTrigs()
{
  PrintFnStart("load marked trigs from file 'markedtrigs.ng'");
  ifstream fin("markedtrigs.ng");

  int n;
  fin >> n;
  if (n != GetNT() || n == 0) {PrintError("Not a suitable marked-trig-file!"); return;}

  int i, m;
  for (i = 1; i <= n; i++)
    {
      fin >> m;
      SetMarkedTrig(i, m);      
    }

  fin >> n;
  if (n != 0) 
    {
 
      Point<3> ap1, ap2;
      for (i = 1; i <= n; i++)
	{
	  fin >> ap1(0); fin >> ap1(1); fin >> ap1(2);
	  fin >> ap2(0); fin >> ap2(1); fin >> ap2(2);
	  AddMarkedSeg(ap1,ap2);      
	}
    }
}

void STLGeometry :: SaveMarkedTrigs()
{
  PrintFnStart("save marked trigs to file 'markedtrigs.ng'");
  ofstream fout("markedtrigs.ng");

  int n = GetNT();
  fout << n << endl;

  int i;
  for (i = 1; i <= n; i++)
    {
      fout << IsMarkedTrig(i) << "\n";
    }

  n = GetNMarkedSegs();
  fout << n << endl;

  Point<3> ap1,ap2;
  for (i = 1; i <= n; i++)
    {
      GetMarkedSeg(i,ap1,ap2);
      fout << ap1(0) << " " << ap1(1) << " " << ap1(2) << "  ";
      fout << ap2(0) << " " << ap2(1) << " " << ap2(2) << " " << "\n";
    }

}

void STLGeometry :: NeighbourAnglesOfSelectedTrig()
{
  int st = GetSelectTrig();

  if (st >= 1 && st <= GetNT())
    {
      int i;
      PrintMessage(1,"Angle to triangle ", st, ":");
      for (i = 1; i <= NONeighbourTrigs(st); i++)
	{
	  PrintMessage(1,"   triangle ", int(NeighbourTrig(st,i)), ": angle = ", 
		       180./M_PI*GetAngle(st, NeighbourTrig(st,i)), "°",
		       ", calculated = ", 180./M_PI*Angle(GetTriangle(st).GeomNormal(points), 
							  GetTriangle(NeighbourTrig(st,i)).GeomNormal(points)), "°");
	}
    }
}

void STLGeometry :: GetVicinity(int starttrig, int size, NgArray<int>& vic)
{
  if (starttrig == 0 || starttrig > GetNT()) {return;} 

  NgArray<int> vicarray;
  vicarray.SetSize(GetNT());

  int i;
  for (i = 1; i <= vicarray.Size(); i++)
    {
      vicarray.Elem(i) = 0;
    }
 
  vicarray.Elem(starttrig) = 1;
  
  int j = 0,k;

  NgArray <int> list1;
  list1.SetSize(0);
  NgArray <int> list2;
  list2.SetSize(0);
  list1.Append(starttrig);

  while (j < size)
    {
      j++;
      for (i = 1; i <= list1.Size(); i++)
	{
	  for (k = 1; k <= NONeighbourTrigs(i); k++)
	    {
	      int nbtrig = NeighbourTrig(list1.Get(i),k);
	      if (nbtrig && vicarray.Get(nbtrig) == 0)
		{
		  list2.Append(nbtrig);
		  vicarray.Elem(nbtrig) = 1;
		}
	    }
	}
      list1.SetSize(0);
      for (i = 1; i <= list2.Size(); i++)
	{
	  list1.Append(list2.Get(i));
	}
      list2.SetSize(0);
    }

  vic.SetSize(0);
  for (i = 1; i <= vicarray.Size(); i++)
    {
      if (vicarray.Get(i)) {vic.Append(i);}
    }
}

void STLGeometry :: CalcVicinity(int starttrig)
{
  if (starttrig == 0 || starttrig > GetNT()) {return;} 

  vicinity.SetSize(GetNT());

  if (!stldoctor.showvicinity) {return;}

  int i;
  for (i = 1; i <= vicinity.Size(); i++)
    {
      vicinity.Elem(i) = 0;
    }
 
  vicinity.Elem(starttrig) = 1;
  
  int j = 0,k;

  NgArray <int> list1;
  list1.SetSize(0);
  NgArray <int> list2;
  list2.SetSize(0);
  list1.Append(starttrig);

  //  int cnt = 1;
  while (j < stldoctor.vicinity)
    {
      j++;
      for (i = 1; i <= list1.Size(); i++)
	{
	  for (k = 1; k <= NONeighbourTrigs(i); k++)
	    {
	      int nbtrig = NeighbourTrig(list1.Get(i),k);
	      if (nbtrig && vicinity.Get(nbtrig) == 0)
		{
		  list2.Append(nbtrig);
		  vicinity.Elem(nbtrig) = 1;
		  //cnt++;
		}
	    }
	}
      list1.SetSize(0);
      for (i = 1; i <= list2.Size(); i++)
	{
	  list1.Append(list2.Get(i));
	}
      list2.SetSize(0);
    }

}

int STLGeometry :: Vicinity(int trig) const 
{
  if (trig <= vicinity.Size() && trig >=1)
    {
      return vicinity.Get(trig);
    }
  else {PrintSysError("In STLGeometry::Vicinity");}
  return 0;
}

void STLGeometry :: InitMarkedTrigs()
{
  markedtrigs.SetSize(GetNT());
  int i;
  for (i = 1; i <= GetNT(); i++)
    {
      SetMarkedTrig(i, 0);
    }
}

void STLGeometry :: MarkDirtyTrigs(const STLParameters& stlparam)
{
  PrintFnStart("mark dirty trigs");
  int i,j;

  markedtrigs.SetSize(GetNT());

  for (i = 1; i <= GetNT(); i++)
    {
      SetMarkedTrig(i, 0);
    }

  int found;
  double dirtyangle = stlparam.yangle/2./180.*M_PI;
  int cnt = 0;
  for (i = 1; i <= GetNT(); i++)
    {
      found = 0;
      for (j = 1; j <= NONeighbourTrigs(i); j++)
	{
	  if (GetAngle(i, NeighbourTrig(i,j)) > dirtyangle)
	    {
	      found++;
	    }
	}
      if (found && GetTriangle(i).MinHeight(points) < 
	  stldoctor.dirtytrigfact*GetTriangle(i).MaxLength(points))
	{
	  SetMarkedTrig(i, 1); cnt++;
	}
      /*
      else if (found == 3)
	{
	  SetMarkedTrig(i, 1); cnt++;	  
	}
      */
    }

  PrintMessage(1, "marked ", cnt, " dirty trigs");
}


void STLGeometry :: MarkTopErrorTrigs()
{
  int cnt = 0;
  markedtrigs.SetSize(GetNT());
  for (int i = 1; i <= GetNT(); i++)
    {
      const STLTriangle & trig = GetTriangle(i);

      SetMarkedTrig(i, trig.flags.toperror);
      if (trig.flags.toperror) cnt++;
    }
  PrintMessage(1,"marked ", cnt, " inconsistent triangles");
}



double STLGeometry :: CalcTrigBadness(int i)
{
  int j;
  double maxbadness = 0;
  STLPointId ap1, ap2;
  for (j = 1; j <= NONeighbourTrigs(i); j++)
    {
      GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), ap1, ap2);
      
      if (!IsEdge(ap1,ap2) && GetGeomAngle(i, NeighbourTrig(i,j)) > maxbadness)
	{
	  maxbadness = GetGeomAngle(i, NeighbourTrig(i,j));
	}
    }
  return maxbadness;

}

void STLGeometry :: GeomSmoothRevertedTrigs(const STLParameters& stlparam)
{
  //double revertedangle = stldoctor.smoothangle/180.*M_PI;
  double fact = stldoctor.dirtytrigfact;

  MarkRevertedTrigs(stlparam);

  int i, j, k, l, p;

  for (i = 1; i <= GetNT(); i++)
    {
      if (IsMarkedTrig(i)) 
	{
	  for (j = 1; j <= 3; j++)
	    {
	      double origbadness = CalcTrigBadness(i);

	      p = GetTriangle(i).PNum(j);
	      Point<3> pm(0.,0.,0.); //Middlevector;
	      Point<3> p0(0.,0.,0.);

	      int cnt = 0;

	      for (k = 1; k <= trigsperpoint.EntrySize(p); k++)
		{
		  const STLTriangle& tr = GetTriangle(trigsperpoint.Get(p,k));
		  for (l = 1; l <= 3; l++)
		    {
		      if (tr.PNum(l) != p)
			{
			  cnt++;
			  pm(0) += GetPoint(tr.PNum(l))(0);
			  pm(1) += GetPoint(tr.PNum(l))(1);
			  pm(2) += GetPoint(tr.PNum(l))(2);
			}
		    }
		}
	      Point3d origp = GetPoint(p);
	      Point3d newp = p0 + fact*(1./(double)cnt)*(pm-p0)+(1.-fact)*(origp-p0);

	      SetPoint(p, newp);

	      if (CalcTrigBadness(i) > 0.9*origbadness) {SetPoint(p,origp); PrintDot('f');}
	      else {PrintDot('s');}
	    }
	}
    }
  MarkRevertedTrigs(stlparam);
}

void STLGeometry :: MarkRevertedTrigs(const STLParameters& stlparam)
{
  int i,j;
  if (edgesperpoint.Size() != GetNP()) {BuildEdges(stlparam);}

  PrintFnStart("mark reverted trigs");

  InitMarkedTrigs();

  int found;
  double revertedangle = stldoctor.smoothangle/180.*M_PI;

  int cnt = 0;
  STLPointId ap1, ap2;
  for (i = 1; i <= GetNT(); i++)
    {
      found = 0;
      for (j = 1; j <= NONeighbourTrigs(i); j++)
	{
	  GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), ap1, ap2);

	  if (!IsEdge(ap1,ap2))
	    {
              if (GetGeomAngle(i, NeighbourTrig(i,j)) > revertedangle)
		{
		  found = 1;
		  break;
		}
	    }
	}
      
      if (found)
	{
	  SetMarkedTrig(i, 1); cnt++;
	}
      
    }

  PrintMessage(5, "found ", cnt, " reverted trigs");


}

void STLGeometry :: SmoothDirtyTrigs(const STLParameters& stlparam)
{
  PrintFnStart("smooth dirty trigs");

  MarkDirtyTrigs(stlparam);

  int i,j;
  int changed = 1;
  STLPointId ap1, ap2;
  
  while (changed)
    {
      changed = 0;
      for (i = 1; i <= GetNT(); i++)
	{
	  if (IsMarkedTrig(i))
	    {
	      int foundtrig = 0;
	      double maxlen = 0;
	      // JS: darf normalvector nicht ueber kurze Seite erben
	      maxlen = GetTriangle(i).MaxLength(GetPoints()) / 2.1; //JG: bei flachem dreieck auch kurze Seite

	      for (j = 1; j <= NONeighbourTrigs(i); j++)
		{
		  if (!IsMarkedTrig(NeighbourTrig(i,j)))
		    {
		      GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)),ap1,ap2);
		      if (Dist(GetPoint(ap1),GetPoint(ap2)) >= maxlen)
			{
			  foundtrig = NeighbourTrig(i,j);
			  maxlen = Dist(GetPoint(ap1),GetPoint(ap2));
			}
		    }
		}
	      if (foundtrig)
		{
		  GetTriangle(i).SetNormal(GetTriangle(foundtrig).Normal());
		  changed = 1;
		  SetMarkedTrig(i,0);
		}
	    }
	}
    }

  calcedgedataanglesnew = 1;


  MarkDirtyTrigs(stlparam);

  int cnt = 0;
  for (i = 1; i <= GetNT(); i++)
    {
      if (IsMarkedTrig(i)) {cnt++;}
    }

  PrintMessage(5,"NO marked dirty trigs=", cnt);

}

int STLGeometry :: IsMarkedTrig(int trig) const 
{
  if (trig <= markedtrigs.Size() && trig >=1)
    {
      return markedtrigs.Get(trig);
    }
  else {PrintSysError("In STLGeometry::IsMarkedTrig");}

  return 0;  
}

void STLGeometry :: SetMarkedTrig(int trig, int num)
{
  if (trig <= markedtrigs.Size() && trig >=1)
    {
      markedtrigs.Elem(trig) = num;
    }
  else {PrintSysError("In STLGeometry::SetMarkedTrig");}
}

void STLGeometry :: Clear()
{
  PrintFnStart("Clear");

  surfacemeshed = 0;
  surfaceoptimized = 0;
  volumemeshed = 0;

  selectedmultiedge.SetSize(0);
  meshlines.SetSize(0);
  // neighbourtrigs.SetSize(0);
  outerchartspertrig.SetSize(0);
  atlas.SetSize(0);
  ClearMarkedSegs();
  ClearSpiralPoints();
  ClearLineEndPoints();

  SetSelectTrig(0);
  SetNodeOfSelTrig(1);
  facecnt = 0;

  SetThreadPercent(100.);

  ClearEdges();
}

double STLGeometry :: Area()
{
  if (area >= 0) return area;
  area = 0;
  for (int i = 1; i <= GetNT(); i++)
    area += GetTriangle(i).Area(points);
  return area;
}

double STLGeometry :: GetAngle(int t1, int t2)
{
  return Angle(GetTriangle(t1).Normal(),GetTriangle(t2).Normal());
}

double STLGeometry :: GetGeomAngle(int t1, int t2)
{
  Vec3d n1 = GetTriangle(t1).GeomNormal(points);
  Vec3d n2 = GetTriangle(t2).GeomNormal(points);
  return Angle(n1,n2);
}


void STLGeometry :: InitSTLGeometry(const NgArray<STLReadTriangle> & readtrias)
{
  PrintFnStart("Init STL Geometry");
  STLTopology::InitSTLGeometry(readtrias);

  int i, k;

  //const double geometry_tol_fact = 1E8; //distances lower than max_box_size/tol are ignored

  int np = GetNP();
  PrintMessage(5,"NO points= ", GetNP());
  normals.SetSize(GetNP());
  NgArray<int> normal_cnt(GetNP()); // counts number of added normals in a point

  for (i = 1; i <= np; i++)
    {
      normal_cnt.Elem(i) = 0;
      normals.Elem(i) = Vec3d (0,0,0);
    }

  for(i = 1; i <= GetNT(); i++)
    {
      //      STLReadTriangle t = GetReadTriangle(i);
      //      STLTriangle st;

      Vec<3> n = GetTriangle(i).Normal ();

      for (k = 1; k <= 3; k++)
	{
	  int pi = GetTriangle(i).PNum(k);
	  
	  normal_cnt.Elem(pi)++;
	  SetNormal(pi, GetNormal(pi) + n);
	}
    } 

  //normalize the normals
  for (i = 1; i <= GetNP(); i++)
    {
      SetNormal(i,1./(double)normal_cnt.Get(i)*GetNormal(i));
    }

  trigsconverted = 1;

  vicinity.SetSize(GetNT());
  markedtrigs.SetSize(GetNT());
  for (i = 1; i <= GetNT(); i++)
    {
      markedtrigs.Elem(i) = 0;
      vicinity.Elem(i) = 1;
    }

  ha_points.SetSize(GetNP());
  for (i = 1; i <= GetNP(); i++)
    ha_points.Elem(i) = 0;

  calcedgedataanglesnew = 0;
  edgedatastored = 0;
  edgedata->Clear();


  if (GetStatus() == STL_ERROR) return;

  CalcEdgeData();
  CalcEdgeDataAngles();

  ClearLineEndPoints();

  CheckGeometryOverlapping();
}

void STLGeometry :: TopologyChanged()
{
  calcedgedataanglesnew = 1;
}

int STLGeometry :: CheckGeometryOverlapping()
{
  PrintMessageCR(3,"Check overlapping geometry ...");

  Box<3> geombox = GetBoundingBox();
  Point<3> pmin = geombox.PMin();
  Point<3> pmax = geombox.PMax();

  BoxTree<3> setree(pmin, pmax);

  int oltrigs = 0;
  markedtrigs.SetSize(GetNT());

  for (int i = 1; i <= GetNT(); i++)
    SetMarkedTrig(i, 0);

  for (int i = 1; i <= GetNT(); i++)
    {
      const STLTriangle & tri = GetTriangle(i);
      
      Point<3> tpmin = tri.box.PMin();
      Point<3> tpmax = tri.box.PMax();
      Vec<3> diag = tpmax - tpmin;

      tpmax = tpmax + 0.001 * diag;
      tpmin = tpmin - 0.001 * diag;

      setree.Insert (tpmin, tpmax, i);
    }


  {
    mutex inters_mutex;
    
    ParallelFor( 1, GetNT()+1, [&] (int first, int next)
                 {
                   NgArray<int> inters;
                   for (int i=first; i<next; i++) {
                     const STLTriangle & tri = GetTriangle(i);
	
                     Point<3> tpmin = tri.box.PMin();
                     Point<3> tpmax = tri.box.PMax();
	
                     setree.GetIntersecting (tpmin, tpmax, inters);
	
                     for (int j = 1; j <= inters.Size(); j++)
                       {
                         const STLTriangle & tri2 = GetTriangle(inters.Get(j));
	    
                         const Point<3> *trip1[3], *trip2[3];	
                         Point<3> hptri1[3], hptri2[3];
                         /*
                           for (k = 1; k <= 3; k++)
                           {
                           trip1[k-1] = &GetPoint (tri.PNum(k));
                           trip2[k-1] = &GetPoint (tri2.PNum(k));
                           }
                         */
	    
                         for (int k = 0; k < 3; k++)
                           {
                             hptri1[k] = GetPoint (tri[k]);
                             hptri2[k] = GetPoint (tri2[k]);
                             trip1[k] = &hptri1[k];
                             trip2[k] = &hptri2[k];
                           }

                         if (IntersectTriangleTriangle (&trip1[0], &trip2[0]))
                           {
                             lock_guard<mutex> guard(inters_mutex);
                             {
                               oltrigs++;
                               PrintMessage(5,"Intersecting Triangles: trig ",i," with ",inters.Get(j),"!");
                               SetMarkedTrig(i, 1);
                               SetMarkedTrig(inters.Get(j), 1);
                             }
                           }
                       }
                   }
                 });
  }
  PrintMessage(3,"Check overlapping geometry ... ", oltrigs, " triangles overlap");
  return oltrigs;
}

/*
  void STLGeometry :: InitSTLGeometry()
  {
  STLTopology::InitSTLGeometry();

  int i, j, k;

  const double geometry_tol_fact = 1E8; //distances lower than max_box_size/tol are ignored


  trias.SetSize(0);
  points.SetSize(0);
  normals.SetSize(0);

  NgArray<int> normal_cnt; // counts number of added normals in a point

  Box3d bb (GetBoundingBox().PMin() + Vec3d (-1,-1,-1),
  GetBoundingBox().PMax() + Vec3d (1, 1, 1));

  Point3dTree pointtree (bb.PMin(), 
  bb.PMax());
  NgArray<int> pintersect;

  double gtol = GetBoundingBox().CalcDiam()/geometry_tol_fact;

  for(i = 1; i <= GetReadNT(); i++)
  {
  //if (i%500==499) {(*mycout) << (double)i/(double)GetReadNT()*100. << "%" << endl;}

  STLReadTriangle t = GetReadTriangle(i);
  STLTriangle st;
  Vec3d n = t.normal;

  for (k = 0; k < 3; k++)
  {
  Point3d p = t.pts[k];

  Point3d pmin = p - Vec3d (gtol, gtol, gtol);
  Point3d pmax = p + Vec3d (gtol, gtol, gtol);
	  
  pointtree.GetIntersecting (pmin, pmax, pintersect);
	  
  if (pintersect.Size() > 1)
  (*mycout) << "found too much  " << char(7) << endl;
  int foundpos = 0;
  if (pintersect.Size())
  foundpos = pintersect.Get(1);

  if (foundpos) 
  {
  normal_cnt[foundpos]++;
  SetNormal(foundpos,GetNormal(foundpos)+n);
  //	      (*testout) << "found p " << p << endl;
  }
  else
  {
  foundpos = AddPoint(p);
  AddNormal(n);
  normal_cnt.Append(1);

  pointtree.Insert (p, foundpos);
  }
  //(*mycout) << "foundpos=" << foundpos << endl;
  st.pts[k] = foundpos;
  }

  if ( (st.pts[0] == st.pts[1]) || 
  (st.pts[0] == st.pts[2]) || 
  (st.pts[1] == st.pts[2]) )
  {
  (*mycout) << "ERROR: STL Triangle degenerated" << endl;
  }
  else
  {
  // do not add ? js
  AddTriangle(st);
  }
  //(*mycout) << "TRIG" << i << " = " << st << endl;
      
  } 
  //normal the normals
  for (i = 1; i <= GetNP(); i++)
  {
  SetNormal(i,1./(double)normal_cnt[i]*GetNormal(i));
  }

  trigsconverted = 1;

  vicinity.SetSize(GetNT());
  markedtrigs.SetSize(GetNT());
  for (i = 1; i <= GetNT(); i++)
  {
  markedtrigs.Elem(i) = 0;
  vicinity.Elem(i) = 1;
  }

  ha_points.SetSize(GetNP());
  for (i = 1; i <= GetNP(); i++)
  ha_points.Elem(i) = 0;

  calcedgedataanglesnew = 0;
  edgedatastored = 0;
  edgedata->Clear();

  CalcEdgeData();
  CalcEdgeDataAngles();

  ClearLineEndPoints();

  (*mycout) << "done" << endl;
  }
*/



void STLGeometry :: SetLineEndPoint(int pn) 
{
  if (pn <1 || pn > lineendpoints.Size()) {PrintSysError("Illegal pnum in SetLineEndPoint!!!"); return; }
  lineendpoints.Elem(pn) = 1;
}

int STLGeometry :: IsLineEndPoint(int pn) 
{
  //  return 0;
  if (pn <1 || pn > lineendpoints.Size()) 
    {PrintSysError("Illegal pnum in IsLineEndPoint!!!"); return 0;}
  return lineendpoints.Get(pn);
}

void STLGeometry :: ClearLineEndPoints()
{
  lineendpoints.SetSize(GetNP());
  int i;
  for (i = 1; i <= GetNP(); i++)
    {
      lineendpoints.Elem(i) = 0;
    }
}

int STLGeometry :: IsEdge(int ap1, int ap2)
{
  int i,j;
  for (i = 1; i <= GetNEPP(ap1); i++)
    {
      for (j = 1; j <= GetNEPP(ap2); j++)
	{
	  if (GetEdgePP(ap1,i) == GetEdgePP(ap2,j)) {return 1;}
	}
    }
  return 0;
}

int STLGeometry :: IsEdgeNum(int ap1, int ap2)
{
  int i,j;
  for (i = 1; i <= GetNEPP(ap1); i++)
    {
      for (j = 1; j <= GetNEPP(ap2); j++)
	{
	  if (GetEdgePP(ap1,i) == GetEdgePP(ap2,j)) {return GetEdgePP(ap1,i);}
	}
    }
  return 0;
}


void STLGeometry :: BuildEdges(const STLParameters& stlparam)
{
  //PrintFnStart("build edges");
  edges.SetSize(0);
  meshlines.SetSize(0);
  FindEdgesFromAngles(stlparam);
}

void STLGeometry :: UseExternalEdges()
{
  for (int i = 1; i <= NOExternalEdges(); i++)
    AddEdge(GetExternalEdge(i).i1,GetExternalEdge(i).i2);
  //BuildEdgesPerPointy();
}

void STLGeometry :: UndoEdgeChange()
{
  if (edgedatastored) 
    {
      RestoreEdgeData();
    }
  else
    {
      PrintWarning("no edge undo possible");
    }
}


void STLGeometry :: StoreEdgeData()
{
  //  edgedata_store = *edgedata;
  
  edgedata->Store();
  edgedatastored = 1;

  // put stlgeom-edgedata to stltopology edgedata 
  /*
    int i;
    for (i = 1; i <= GetNTE(); i++)
    {
    const STLTopEdge & topedge = GetTopEdge (i);
    int ednum = edgedata->GetEdgeNum (topedge.PNum(1),
    topedge.PNum(2));
    topedges.Elem(i).SetStatus (edgedata->Get (ednum).status);
    }
  */
}

void STLGeometry :: RestoreEdgeData()
{
  //  *edgedata = edgedata_store;
  edgedata->Restore();
  edgedatastored=0;
}


void STLGeometry :: CalcEdgeData()
{
  PushStatus("Calc Edge Data");

  STLPointId np1, np2;
  
  int ecnt = 0;
  edgedata->SetSize(GetNT()/2*3);

  for (int i = 1; i <= GetNT(); i++)
    {
      SetThreadPercent((double)i/(double)GetNT()*100.);
      
      const STLTriangle & t1 = GetTriangle(i);

      for (int j = 1; j <= NONeighbourTrigs(i); j++)
	{
	  int nbti = NeighbourTrig(i,j);
	  if (nbti > i)
	    {
	      const STLTriangle & t2 = GetTriangle(nbti);

	      if (t1.IsNeighbourFrom(t2))
		{
		  ecnt++; if (ecnt > edgedata->Size()) {PrintError("In Calc edge data, illegal geometry");}

		  t1.GetNeighbourPoints(t2,np1,np2);

		  /* ang = GetAngle(i,nbti);
		     if (ang < -M_PI) {ang += 2*M_PI;}*/


		  // edgedata->Add(STLEdgeData(0, np1, np2, i, nbti),ecnt);
		  edgedata->Elem(ecnt).SetStatus(ED_UNDEFINED);

		  // edgedata->Elem(ecnt).top = this;
		  // edgedata->Elem(ecnt).topedgenr = GetTopEdgeNum (np1, np2);
		}
	    }
	}      
    }
  
  //BuildEdgesPerPoint();
  PopStatus();  
}

void STLGeometry :: CalcEdgeDataAngles()
{
  PrintMessageCR (5,"calc edge data angles ... ");

  for (int i = 1; i <= GetNTE(); i++)
    {
      STLTopEdge & edge = GetTopEdge (i);
      double cosang = edge.TrigNum(2) == 0 ? 1. :
	GetTriangle(edge.TrigNum(1)).Normal() *
	GetTriangle(edge.TrigNum(2)).Normal();
      edge.SetCosAngle (cosang);
    }

  for (int i = 1; i <= edgedata->Size(); i++)
    {
      /*
      const STLEdgeData& e = edgedata->Get(i);
      ang = GetAngle(e.lt,e.rt);
      if (ang < -M_PI) {ang += 2*M_PI;}
      edgedata->Elem(i).angle = fabs(ang);
      */
    }
  PrintMessage (5,"calc edge data angles ... done");
}

void STLGeometry :: FindEdgesFromAngles(const STLParameters& stlparam)
{
  //  PrintFnStart("find edges from angles");

  double min_edge_angle = stlparam.yangle/180.*M_PI;
  double cont_min_edge_angle = stlparam.contyangle/180.*M_PI;

  double cos_min_edge_angle = cos (min_edge_angle);
  double cos_cont_min_edge_angle = cos (cont_min_edge_angle);

  if (calcedgedataanglesnew) {CalcEdgeDataAngles(); calcedgedataanglesnew = 0;}

  for (int i = 1; i <= edgedata->Size(); i++)
    {
      STLTopEdge & sed = edgedata->Elem(i);
      if(sed.TrigNum(2) == 0)
        sed.SetStatus(ED_CONFIRMED);
      if (sed.GetStatus() == ED_CANDIDATE || 
	  sed.GetStatus() == ED_UNDEFINED)
	{
	  if (sed.CosAngle() <= cos_min_edge_angle)
	    {
	      sed.SetStatus (ED_CANDIDATE);
	    }
	  else
	    {
	      sed.SetStatus(ED_UNDEFINED);
	    }
	} 
    }

  if (stlparam.contyangle < stlparam.yangle)
    {
      int changed = 1;
      [[maybe_unused]] int its = 0;
      while (changed && stlparam.contyangle < stlparam.yangle)
	{
          its++;
	  //(*mycout) << "." << flush;
	  changed = 0;
	  for (int i = 1; i <= edgedata->Size(); i++)
	    {
	      STLTopEdge & sed = edgedata->Elem(i);
	      if (sed.CosAngle() <= cos_cont_min_edge_angle 
		  && sed.GetStatus() == ED_UNDEFINED && 
		  (edgedata->GetNConfCandEPP(sed.PNum(1)) == 1 || 
		   edgedata->GetNConfCandEPP(sed.PNum(2)) == 1))
		{
		  changed = 1;
		  sed.SetStatus (ED_CANDIDATE);
		}
	    }
	}
    }
  
  int confcand = 0;
  if (edgedata->GetNConfEdges() == 0) 
    {
      confcand = 1;
    }
  
  for (int i = 1; i <= edgedata->Size(); i++)
    {
      STLTopEdge & sed = edgedata->Elem(i);
      if (sed.GetStatus() == ED_CONFIRMED || 
	  (sed.GetStatus() == ED_CANDIDATE && confcand))
	{
	  STLEdge se(sed.PNum(1),sed.PNum(2));
	  se.SetLeftTrig(sed.TrigNum(1));
	  se.SetRightTrig(sed.TrigNum(2));
	  AddEdge(se);
	}
    }
  BuildEdgesPerPoint();

  

  //(*mycout) << "its for continued angle = " << its << endl;
  PrintMessage(5,"built ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");
  
}

/*
void STLGeometry :: FindEdgesFromAngles()
{
  double yangle = stlparam.yangle;
  char * savetask = multithread.task;
  multithread.task = "find edges";

  const double min_edge_angle = yangle/180.*M_PI;

  int np1, np2;
  double ang;
  int i;

  //(*mycout) << "area=" << Area() << endl;

  for (i = 1; i <= GetNT(); i++)
    {
      multithread.percent = (double)i/(double)GetReadNT()*100.;
      
      const STLTriangle & t1 = GetTriangle(i);
      //NeighbourTrigs(nt,i);

      for (int j = 1; j <= NONeighbourTrigs(i); j++)
	{
	  int nbti = NeighbourTrig(i,j);
	  if (nbti > i)
	    {
	      const STLTriangle & t2 = GetTriangle(nbti);

	      if (t1.IsNeighbourFrom(t2))
		{
		  ang = GetAngle(i,nbti);
		  if (ang < -M_PI*0.5) {ang += 2*M_PI;}

		  t1.GetNeighbourPoints(t2,np1,np2);
		  
		  if (fabs(ang) >= min_edge_angle)
		    {
		      STLEdge se(np1,np2);
		      se.SetLeftTrig(i);
		      se.SetRightTrig(nbti);
		      AddEdge(se);
		    }
		}
	    }
	}      
    }
  
  (*mycout) << "added " << GetNE() << " edges" << endl;

  //BuildEdgesPerPoint();

  multithread.percent = 100.;
  multithread.task = savetask;
  
}
*/
void STLGeometry :: BuildEdgesPerPoint()
{
  //cout << "*** build edges per point" << endl;
  edgesperpoint.SetSize(GetNP());

  //add edges to points
  for (int i = 1; i <= GetNE(); i++)
    {
      //(*mycout) << "EDGE " << GetEdge(i).PNum(1) << " - " << GetEdge(i).PNum(2) << endl;
      for (int j = 1; j <= 2; j++)
	{
	  AddEdgePP(GetEdge(i).PNum(j),i);
	}
    }
}

void STLGeometry :: AddFaceEdges()
{
  PrintFnStart("Add starting edges for faces");

  //für Kugel eine STLLine hinzufügen (Vorteil: verfeinerbar, unabhängig von Auflösung der Geometrie!!!):
  //Grenze von 1. gefundener chart

  NgArray<int> edgecnt;
  NgArray<int> chartindex;
  edgecnt.SetSize(GetNOFaces());
  chartindex.SetSize(GetNOFaces());

  for (int i = 1; i <= GetNOFaces(); i++)
    {
      edgecnt.Elem(i) = 0;
      chartindex.Elem(i) = 0;
    }

  for (int i = 1; i <= GetNT(); i++)
    {
      int fn = GetTriangle(i).GetFaceNum();
      if (!chartindex.Get(fn)) {chartindex.Elem(fn) = GetChartNr(i);}
      for (int j = 1; j <= 3; j++)
	{
	  edgecnt.Elem(fn) += GetNEPP(GetTriangle(i).PNum(j));
	}
    }

  for (int i = 1; i <= GetNOFaces(); i++)
    {
      if (!edgecnt.Get(i)) {PrintMessage(5,"Face", i, " has no edge!");}
    }
  
  int changed = 0;
  STLPointId ap1, ap2;
  for (int i = 1; i <= GetNOFaces(); i++)
    {
      if (!edgecnt.Get(i))
      {
	const STLChart& c = GetChart(chartindex.Get(i));
        // bool foundone = false;
        int longest_ap1 = -1, longest_ap2 = -1;
        double maxlen = -1;
	for (int j = 1; j <= c.GetNChartT(); j++)
	  {
	    const STLTriangle& t1 = GetTriangle(c.GetChartTrig1(j));
	    for (int k = 1; k <= 3; k++)
	      {
		int nt = NeighbourTrig(c.GetChartTrig1(j),k);
		if (GetChartNr(nt) != chartindex.Get(i))
		  {
		    t1.GetNeighbourPoints(GetTriangle(nt),ap1,ap2);
                    // AddEdge(ap1,ap2);
                    double len = Dist(GetPoint(ap1), GetPoint(ap2));
                    if (len > maxlen) 
                      {
                        maxlen = len;
                        longest_ap1 = ap1;
                        longest_ap2 = ap2;
                      }
		    changed = 1;
		  }
	      }
	  }
        if (maxlen > 0)
          AddEdge(longest_ap1,longest_ap2);
      }
      
    }
  
  if (changed) BuildEdgesPerPoint();
  
}

void STLGeometry :: LinkEdges(const STLParameters& stlparam)
{
  PushStatusF("Link Edges");
  PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");

  int i;

  lines.SetSize(0);
  int starte(0);
  int edgecnt = 0;
  int found;
  int rev(0); //indicates, that edge is inserted reverse

  //worked edges
  NgArray<int> we(GetNE());

  //setlineendpoints; wenn 180°, dann keine endpunkte
  //nur punkte mit 2 edges kommen in frage, da bei mehr oder weniger punkten ohnehin ein meshpoint hinkommt

  Vec3d v1,v2;
  double cos_eca = cos(stlparam.edgecornerangle/180.*M_PI);
  int ecnt = 0;
  int lp1, lp2;
  if (stlparam.edgecornerangle < 180)
    {
      for (i = 1; i <= GetNP(); i++)
	{
	  if (GetNEPP(i) == 2)
	    {
	      if (GetEdge(GetEdgePP(i,1)).PNum(2) == GetEdge(GetEdgePP(i,2)).PNum(1) ||
		  GetEdge(GetEdgePP(i,1)).PNum(1) == GetEdge(GetEdgePP(i,2)).PNum(2))
		{
		  lp1 = 1; lp2 = 2;
		}
	      else
		{
		  lp1 = 2; lp2 = 1;
		}

	      v1 = Vec3d(GetPoint(GetEdge(GetEdgePP(i,1)).PNum(1)),
			 GetPoint(GetEdge(GetEdgePP(i,1)).PNum(2)));
	      v2 = Vec3d(GetPoint(GetEdge(GetEdgePP(i,2)).PNum(lp1)),
			 GetPoint(GetEdge(GetEdgePP(i,2)).PNum(lp2)));
	      if ((v1*v2)/sqrt(v1.Length2()*v2.Length2()) < cos_eca) 
		{
		  //(*testout) << "add edgepoint " << i << endl;
		  SetLineEndPoint(i);
		  ecnt++;
		}
	    }	  
	}
    }
  PrintMessage(5, "added ", ecnt, " mesh_points due to edge corner angle (", 
	       stlparam.edgecornerangle, " degree)");

  for (i = 1; i <= GetNE(); i++) {we.Elem(i) = 0;}

  while(edgecnt < GetNE())
    {
      SetThreadPercent((double)edgecnt/(double)GetNE()*100.);

      STLLine* line = new STLLine(this);

      //find start edge
      int j = 1;
      found = 0;
      //try second time, if only rings are left!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      int second = 0;

      //find a starting edge at point with 1 or more than 2 edges or at lineendpoint
      while (!found && j<=GetNE())
	{
	  if (!we.Get(j))
	    {
	      if (GetNEPP(GetEdge(j).PNum(1)) != 2 || IsLineEndPoint(GetEdge(j).PNum(1)))
		{
		  starte = j;
		  found = 1;
		  rev = 0;
		}
	      else 
	      if (GetNEPP(GetEdge(j).PNum(2)) != 2 || IsLineEndPoint(GetEdge(j).PNum(2)))
		{
		  starte = j;
		  found = 1;
		  rev = 1;
		}
	      else if (second)
		{
		  starte = j;
		  found = 1;
		  rev = 0; //0 or 1 are possible
		}
	    }
	  j++;
	  if (!second && j == GetNE()) {second = 1; j = 1;}
	}

      if (!found) {PrintSysError("No starting edge found, edgecnt=", edgecnt, ", GETNE=", GetNE());}

      line->AddPoint(GetEdge(starte).PNum(1+rev));
      line->AddPoint(GetEdge(starte).PNum(2-rev));
      if (!rev)
	{
	  line->AddLeftTrig(GetEdge(starte).LeftTrig());
	  line->AddRightTrig(GetEdge(starte).RightTrig());
	}
      else
	{
	  line->AddLeftTrig(GetEdge(starte).RightTrig());
	  line->AddRightTrig(GetEdge(starte).LeftTrig());
	}
      edgecnt++; we.Elem(starte) = 1;

      //add segments to line as long as segments other than starting edge are found or lineendpoint is reached 
      found = 1;
      int other;
      while(found)
	{
	  found = 0;
	  int fp = GetEdge(starte).PNum(2-rev);
	  if (GetNEPP(fp) == 2 && !IsLineEndPoint(fp))
	    {
	      //find the "other" edge of point fp
	      other = 0;
	      if (GetEdgePP(fp,1) == starte) {other = 1;}

	      starte = GetEdgePP(fp,1+other);

	      //falls ring -> aufhoeren !!!!!!!!!!!
	      if (!we.Elem(starte))
		{
		  found = 1;
		  rev = 0;
		  if (GetEdge(starte).PNum(2) == fp) {rev = 1;}
		  else if (GetEdge(starte).PNum(1) != fp) {PrintSysError("In Link Edges!");}

		  line->AddPoint(GetEdge(starte).PNum(2-rev));
		  if (!rev) 
		    {
		      line->AddLeftTrig(GetEdge(starte).LeftTrig());
		      line->AddRightTrig(GetEdge(starte).RightTrig());
		    }
		  else
		    {
		      line->AddLeftTrig(GetEdge(starte).RightTrig());
		      line->AddRightTrig(GetEdge(starte).LeftTrig());
		    }
		  edgecnt++; we.Elem(starte) = 1;
		}
	    }     
	}
      AddLine(line);      
    }
  PrintMessage(5,"number of lines generated = ", GetNLines());

  //check, which lines must have at least one midpoint
  INDEX_2_HASHTABLE<int> lineht(GetNLines()+1);

  for (i = 1; i <= GetNLines(); i++)
    {
      if (GetLine(i)->StartP() == GetLine(i)->EndP())
	{
	  GetLine(i)->DoSplit();	  
	}
    }

  for (i = 1; i <= GetNLines(); i++)
    {
      INDEX_2 lineep (GetLine(i)->StartP(),GetLine(i)->EndP());
      lineep.Sort();

      if (lineht.Used (lineep))
	{
	  GetLine(i)->DoSplit();
	  int other = lineht.Get(lineep);
	  GetLine(other)->DoSplit();
	}
      else
	{
	  lineht.Set (lineep, i);
	}
    }

  for (i = 1; i <= GetNLines(); i++)
    {
      STLLine* line = GetLine(i);
      for (int ii = 1; ii <= line->GetNS(); ii++)
	{
	  int ap1, ap2;
	  line->GetSeg(ii,ap1,ap2);
	  //	  (*mycout) << "SEG " << p1 << " - " << p2 << endl;
	}
    }

  PopStatus();
}

int STLGeometry :: GetNOBodys()
{
  int markedtrigs1 = 0;
  int starttrig = 1;
  int i, k, nnt;
  int bodycnt = 0;

  NgArray<int> bodynum(GetNT());

  for (i = 1; i <= GetNT(); i++)
    bodynum.Elem(i)=0;


  while (markedtrigs1 < GetNT())
    {
      for (i = starttrig; i <= GetNT(); i++)
	{
	  if (!bodynum.Get(i))
	    {
	      starttrig = i;
	      break;
	    }
	} 
      //add all triangles around starttriangle, which is reachable without going over an edge
      NgArray<int> todolist;
      NgArray<int> nextlist;
      bodycnt++;
      markedtrigs1++;
      bodynum.Elem(starttrig) = bodycnt;
      todolist.Append(starttrig);

      while(todolist.Size())
	{
	  for (i = 1; i <= todolist.Size(); i++)
	    {
	      //const STLTriangle& tt = GetTriangle(todolist.Get(i));
	      for (k = 1; k <= NONeighbourTrigs(todolist.Get(i)); k++)
		{
		  nnt = NeighbourTrig(todolist.Get(i),k);
		  if (!bodynum.Get(nnt))
		    {
		      nextlist.Append(nnt);
		      bodynum.Elem(nnt) = bodycnt;
		      markedtrigs1++;
		    }
		}
	    }
	  
	  todolist.SetSize(0);
	  for (i = 1; i <= nextlist.Size(); i++)
	    {
	      todolist.Append(nextlist.Get(i));
	    }
	  nextlist.SetSize(0);	  
	}
    }
  PrintMessage(3, "Geometry has ", bodycnt, " separated bodys");

  return bodycnt;
}

void STLGeometry :: CalcFaceNums()
{
  int markedtrigs1 = 0;
  int starttrig(0);
  int laststarttrig = 1;
  int i, k, nnt;
  facecnt = 0;


  for (i = 1; i <= GetNT(); i++)
    GetTriangle(i).SetFaceNum(0);


  while (markedtrigs1 < GetNT())
    {
      for (i = laststarttrig; i <= GetNT(); i++)
	{
	  if (!GetTriangle(i).GetFaceNum()) 
	    {
	      starttrig = i;
	      laststarttrig = i;
	      break;
	    }
	} 
      //add all triangles around starttriangle, which is reachable without going over an edge
      NgArray<int> todolist;
      NgArray<int> nextlist;
      facecnt++;
      markedtrigs1++;
      GetTriangle(starttrig).SetFaceNum(facecnt);
      todolist.Append(starttrig);
      STLPointId ap1, ap2;

      while(todolist.Size())
	{
	  for (i = 1; i <= todolist.Size(); i++)
	    {
	      const STLTriangle& tt = GetTriangle(todolist.Get(i));
	      for (k = 1; k <= NONeighbourTrigs(todolist.Get(i)); k++)
		{
		  nnt = NeighbourTrig(todolist.Get(i),k);
		  STLTriangle& nt = GetTriangle(nnt);
		  if (!nt.GetFaceNum())
		    {
		      tt.GetNeighbourPoints(nt,ap1,ap2);
		      if (!IsEdge(ap1,ap2))
			{
			  nextlist.Append(nnt);
			  nt.SetFaceNum(facecnt);
			  markedtrigs1++;
			}
		    }
		}
	    }
	  
	  todolist.SetSize(0);
	  for (i = 1; i <= nextlist.Size(); i++)
	    {
	      todolist.Append(nextlist.Get(i));
	    }
	  nextlist.SetSize(0);	  
	}
    }
  GetNOBodys();
  PrintMessage(3,"generated ", facecnt, " faces");
}
 
void STLGeometry :: ClearSpiralPoints()
{
  spiralpoints.SetSize(GetNP());
  int i;
  for (i = 1; i <= spiralpoints.Size(); i++)
    {
      spiralpoints.Elem(i) = 0;
    }
}


void STLGeometry :: BuildSmoothEdges ()
{
  if (smoothedges) delete smoothedges;

  smoothedges = new INDEX_2_HASHTABLE<int> (GetNE()/10 + 1);


  // Jack: Ok ?
  //  UseExternalEdges();

  PushStatusF("Build Smooth Edges");

  int nt = GetNT();
  Vec3d ng1, ng2;

  for (int i = 1; i <= nt; i++)
    {
      if (multithread.terminate)
	{PopStatus();return;}

      SetThreadPercent(100.0 * (double)i / (double)nt);

      const STLTriangle & trig = GetTriangle (i);
      
      ng1 = trig.GeomNormal(points);
      ng1 /= (ng1.Length() + 1e-24);

      for (int j = 1; j <= NONeighbourTrigs(i); j++)
	{ 
	  int nbt = NeighbourTrig (i, j);

	  ng2 = GetTriangle(nbt).GeomNormal(points);
	  ng2 /= (ng2.Length() + 1e-24);
	  
	  STLPointId pi1, pi2;
	  trig.GetNeighbourPoints(GetTriangle(nbt), pi1, pi2);

	  if (!IsEdge(pi1,pi2)) 
	    {
	      if (ng1 * ng2 < 0)
		{
		  PrintMessage(7,"smoothedge found");
		  INDEX_2 i2(pi1, pi2);
		  i2.Sort();
		  smoothedges->Set (i2, 1);
		}
	    }
	}
    }
  PopStatus();
}





bool STLGeometry :: IsSmoothEdge (int pi1, int pi2) const
{
  if (!smoothedges)
    return false;
  INDEX_2 i2(pi1, pi2);
  i2.Sort();
  return smoothedges->Used (i2);
}



/*
//function is not used now
int IsInArray(int n, const NgArray<int>& ia)
{
  int i;
  for (i = 1; i <= ia.Size(); i++)
    {
      if (ia.Get(i) == n) {return 1;}
    }
  return 0;
}
*/

void STLGeometry :: AddConeAndSpiralEdges(const STLParameters& stlparam)
{
  PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");
  
  PrintFnStart("AddConeAndSpiralEdges");

  // int i,j,k,n;
  //  int changed = 0;

  //check edges, where inner chart and no outer chart come together without an edge
  STLPointId np1, np2;
  int cnt = 0;

  for (ChartId i = 1; i <= GetNOCharts(); i++)
    {
      STLChart& chart = GetChart(i);
      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) != i && !TrigIsInOC(nt,i))
		{	      
		  tt.GetNeighbourPoints(GetTriangle(nt),np1,np2);
		  if (!IsEdge(np1,np2))
		    {
		      STLEdge se(np1,np2);
		      se.SetLeftTrig(t);
		      se.SetRightTrig(nt);
		      int edgenum = AddEdge(se);
		      AddEdgePP(np1,edgenum);
		      AddEdgePP(np2,edgenum);
		      //changed = 1;
		      PrintWarning("Found a spiral like structure: chart=", int(i),
				   ", trig=", int(t), ", p1=", int(np1), ", p2=", int(np2));
		      cnt++;
		    }
		}
	    }
	}
	  
    }

  PrintMessage(5, "found ", cnt, " spiral like structures");
  PrintMessage(5, "added ", cnt, " edges due to spiral like structures");
  
  cnt = 0;
  int edgecnt = 0;

  Array<STLTrigId> trigsaroundp;
  NgArray<int> chartpointchecked(GetNP()); //gets number of chart, if in this chart already checked
  chartpointchecked = 0;


  int onoc, notonoc, tpp, pn;
  STLPointId ap1, ap2;
  int tn1, tn2, l, problem;

  if (!stldoctor.conecheck) {PrintWarning("++++++++++++ \ncone checking deactivated by user!!!!!\n+++++++++++++++"); return ;}

  PushStatus("Find Critical Points");

  int addedges = 0;

  for (ChartId i = 1; i <= GetNOCharts(); i++)
    {
      SetThreadPercent((double)i/(double)GetNOCharts()*100.);
      if (multithread.terminate)
	{PopStatus();return;}

      STLChart& chart = GetChart(i);
      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++)
	    {
	      pn = tt.PNum(k);
	      if (chartpointchecked.Get(pn) == i)
		{continue;}
	      
	      int checkpoint = 0;
	      for (int n = 1; n <= trigsperpoint.EntrySize(pn); n++)
		{
		  if (trigsperpoint.Get(pn,n) != t && 
		      GetChartNr(trigsperpoint.Get(pn,n)) != i &&
		      !TrigIsInOC(trigsperpoint.Get(pn,n),i)) {checkpoint = 1;};
		}
	      if (checkpoint)
		{
		  chartpointchecked.Elem(pn) = i;

		  int worked = 0;
		  int spworked = 0;
		  GetSortedTrianglesAroundPoint(pn,t,trigsaroundp);
		  trigsaroundp.Append(t);
		      
		  problem = 0;
		  for (int l = 2; l <= trigsaroundp.Size()-1; l++)
		    {
		      tn1 = trigsaroundp[l-2];
		      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) != i && !TrigIsInOC(tn2,i)) {problem = 1;}
		    }

		  if (problem)
		    {
		      for (int l = 2; l <= trigsaroundp.Size()-1; l++)
			{
			  tn1 = trigsaroundp[l-2];
			  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(tn1) == i && GetChartNr(tn2) != i && TrigIsInOC(tn2,i)) ||
			      (GetChartNr(tn2) == i && GetChartNr(tn1) != i && TrigIsInOC(tn1,i))) 				 
			    {
			      if (addedges || !GetNEPP(pn))
				{
				  STLEdge se(ap1,ap2);
				  se.SetLeftTrig(tn1);
				  se.SetRightTrig(tn2);
				  int edgenum = AddEdge(se);
				  AddEdgePP(ap1,edgenum);
				  AddEdgePP(ap2,edgenum);
				  edgecnt++;
				}
			      if (!addedges && !GetSpiralPoint(pn))
				{
				  SetSpiralPoint(pn);
				  spworked = 1;
				}
			      worked = 1;
			    }
			}
		    }
		  //backwards:
		  problem = 0;
		  for (int l = trigsaroundp.Size()-1; l >= 2; l--)
		    {
		      tn1 = trigsaroundp[l];
		      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) != i && !TrigIsInOC(tn2,i)) {problem = 1;}
		    }
		  if (problem)
		    for (int l = trigsaroundp.Size()-1; l >= 2; l--)
		      {
			tn1 = trigsaroundp[l];
			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(tn1) == i && GetChartNr(tn2) != i && TrigIsInOC(tn2,i)) ||
			    (GetChartNr(tn2) == i && GetChartNr(tn1) != i && TrigIsInOC(tn1,i))) 				 
			  {
			    if (addedges || !GetNEPP(pn))
			      {
				STLEdge se(ap1,ap2);
				se.SetLeftTrig(tn1);
				se.SetRightTrig(tn2);
				int edgenum = AddEdge(se);
				AddEdgePP(ap1,edgenum);
				AddEdgePP(ap2,edgenum);
				edgecnt++;
			      }
			    if (!addedges && !GetSpiralPoint(pn))
			      {
				SetSpiralPoint(pn);
				spworked = 1;
				//if (GetNEPP(pn) == 0) {(*mycout) << "ERROR: spiralpoint with no edge found!" << endl;}
			      }
			    worked = 1;
			  }
		      }

		  if (worked)
		    {		      
		      //(*testout) << "set edgepoint due to spirals: pn=" << i << endl;
		      SetLineEndPoint(pn);
		    }
		  if (spworked)
		    {		
		      /*      
		      (*mycout) << "Warning: Critical Point " << tt.PNum(k) 
			   << "( chart " << i << ", trig " << t
			   << ") has been neutralized!!!" << endl;
		      */
		      cnt++;
		    }
		  //		  markedpoints.Elem(tt.PNum(k)) = 1;
		}
	    }
	}
    }
  PrintMessage(5, "found ", cnt, " critical points!");
  PrintMessage(5, "added ", edgecnt, " edges due to critical points!");

  PopStatus();

  //search points where inner chart and outer chart and "no chart" trig come together at edge-point

  PrintMessage(7,"search for special chart points");
  for (ChartId i = 1; i <= GetNOCharts(); i++)
    {
      STLChart& chart = GetChart(i);
      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++)
	    {
	      pn = tt.PNum(k);
	      if (GetNEPP(pn) == 2)
		{
		  onoc = 0;
		  notonoc = 0;
		  for (int n = 1; n <= trigsperpoint.EntrySize(pn); n++)
		    {
		      tpp = trigsperpoint.Get(pn,n);
		      if (tpp != t && GetChartNr(tpp) != i)
			{
			  if (TrigIsInOC(tpp,i)) {onoc = 1;}
			  if (!TrigIsInOC(tpp,i)) {notonoc = 1;}
			}
		    }
		  if (onoc && notonoc && !IsLineEndPoint(pn)) 
		    {
		      GetSortedTrianglesAroundPoint(pn,t,trigsaroundp);
		      int here = 1; //we start on this side of edge, !here = there
		      int thereOC = 0;
		      int thereNotOC = 0;
		      for (l = 2; l <= trigsaroundp.Size(); l++)
			{
			  GetTriangle(trigsaroundp[l-2]).
			    GetNeighbourPoints(GetTriangle(trigsaroundp[l-1]), ap1, ap2);
			  if (IsEdge(ap1,ap2)) {here = (here+1)%2;}
			  if (!here && TrigIsInOC(trigsaroundp[l-1],i)) {thereOC = 1;}
			  if (!here && !TrigIsInOC(trigsaroundp[l-1],i)) {thereNotOC = 1;}
			}
		      if (thereOC && thereNotOC)
			{
			  //(*mycout) << "Special OCICnotC - point " << pn << " found!" << endl;
			  //(*testout) << "set edgepoint due to spirals: pn=" << i << endl;
			  SetLineEndPoint(pn);
			}
		    }
		}
	    }
	}
    }
  PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");
}

//get trigs at a point, started with starttrig, then every left
void STLGeometry :: GetSortedTrianglesAroundPoint(STLPointId p, STLTrigId starttrig, Array<STLTrigId>& trigs)
{
  STLTrigId acttrig = starttrig;
  trigs.SetAllocSize(trigsperpoint.EntrySize(p));
  trigs.SetSize(0);
  trigs.Append(acttrig);
  int locindex1(0), locindex2(0);
  //(*mycout) << "trigs around point " << p << endl;

  int end = 0;
  while (!end)
    {
      const STLTriangle& at = GetTriangle(acttrig);
      for (int i = 1; i <= trigsperpoint.EntrySize(p); i++)
	{
	  STLTrigId t = trigsperpoint.Get(p,i);
	  const STLTriangle& nt = GetTriangle(t);
	  if (at.IsNeighbourFrom(nt))
	    {
              STLPointId ap1, ap2;
	      at.GetNeighbourPoints(nt, ap1, ap2);
	      if (ap2 == p) {Swap(ap1,ap2);}
	      if (ap1 != p) {PrintSysError("In GetSortedTrianglesAroundPoint!!!");}
	      
	      for (int j = 1; j <= 3; j++) 
		{
		  if (at.PNum(j) == ap1) {locindex1 = j;};
		  if (at.PNum(j) == ap2) {locindex2 = j;};
		}
	      if ((locindex2+1)%3+1 == locindex1) 
		{
		  if (t != starttrig)
		    {
		      trigs.Append(t);
		      //		      (*mycout) << "trig " << t << endl;
		      acttrig = t;
		    }
		  else
		    {
		      end = 1;
		    }
		  break;
		}
	    }
	}
    }
  
}

/*
int STLGeometry :: NeighbourTrig(int trig, int nr) const
{
  return neighbourtrigs.Get(trig,nr);
}
*/



void STLGeometry :: SmoothGeometry ()
{
  int i, j, k;
  
  double maxerr0, maxerr;

  for (i = 1; i <= GetNP(); i++)
    {
      if (GetNEPP(i)) continue;
      
      maxerr0 = 0;
      for (j = 1; j <= NOTrigsPerPoint(i); j++)
	{
	  int tnum = TrigPerPoint(i, j);
	  double err = Angle (GetTriangle(tnum).Normal (), 
			      GetTriangle(tnum).GeomNormal(GetPoints()));
	  if (err > maxerr0)
	    maxerr0 = err;
	}

      Point3d pi = GetPoint (i);
      if (maxerr0 < 1.1) continue;    // about 60 degree

      maxerr0 /= 2;  // should be at least halfen
      
      for (k = 1; k <= NOTrigsPerPoint(i); k++)
	{
	  const STLTriangle & trig = GetTriangle (TrigPerPoint (i, k));
	  Point3d c = Center(GetPoint (trig.PNum(1)),
			     GetPoint (trig.PNum(2)),
			     GetPoint (trig.PNum(3)));

	  Point3d np = pi + 0.1 * (c - pi);
	  SetPoint (i, np);
	  
	  maxerr = 0;
	  for (j = 1; j <= NOTrigsPerPoint(i); j++)
	    {
	      int tnum = TrigPerPoint(i, j);
	      double err = Angle (GetTriangle(tnum).Normal (), 
				  GetTriangle(tnum).GeomNormal(GetPoints()));
	      if (err > maxerr)
		maxerr = err;
	    }
	  
	  if (maxerr < maxerr0)
	    {
	      pi = np;
	    }
	}

      SetPoint (i, pi);
    }
}

void STLGeometry :: WriteChartToFile( ChartId chartnumber, filesystem::path filename )
{
  PrintMessage(1,"write chart ", int(chartnumber), " to ", filename);
  Array<int> trignums;

  if (chartnumber >= 1 && chartnumber <= GetNOCharts())
  {
    const STLChart& chart = GetChart(chartnumber);

    for (int j = 1; j <= chart.GetNChartT(); j++)
      trignums.Append(chart.GetChartTrig1(j));

    for (int j = 1; j <= chart.GetNOuterT(); j++)
      trignums.Append(chart.GetOuterTrig1(j));

    QuickSort(trignums);
    STLGeometry geo;
    NgArray<STLReadTriangle> readtrigs;
    const auto & first_trig = GetTriangle(chart.GetChartTrig1(1));
    auto normal = first_trig.Normal();
    Box<3> box{Box<3>::EMPTY_BOX};

    for(auto j : trignums)
    {
      const auto& trig = GetTriangle(j);
      Point<3> pts[3];
      for(auto k : Range(3))
      {
        pts[k] = GetPoint(trig[k]);
        box.Add(pts[k]);
      }
      // Vec3d normal = Cross( pts[1]-pts[0], pts[2]-pts[0] );
      readtrigs.Append(STLReadTriangle(pts, trig.Normal()));
    }
    auto dist = box.PMax() - box.PMin();
    auto extra_point = GetPoint(first_trig[0]) - dist.Length()*normal;

    NgArray<int> acttrigs(GetNT());
    acttrigs = -1;
    for (int j = 1; j <= chart.GetNT(); j++)
      acttrigs.Elem(chart.GetTrig1(j)) = chartnumber;

    for (int j = 1; j <= chart.GetNT(); j++)
    {
      auto t = chart.GetTrig1(j);
      const auto & tt = GetTriangle(t);
      for (int k = 1; k <= 3; k++)
      {
        int nt = NeighbourTrig(t,k);
        if (acttrigs.Get(nt) != chartnumber)
        {
          STLPointId np1, np2;
          tt.GetNeighbourPoints(GetTriangle(nt),np1,np2);

          Point<3> pts[3];
          pts[0] = GetPoint(np2);
          pts[1] = GetPoint(np1);
          pts[2] = extra_point;
          Vec3d normal = -Cross( pts[2]-pts[0], pts[1]-pts[0] );
          readtrigs.Append(STLReadTriangle(pts, normal));
        }
      }
    }

    geo.InitSTLGeometry(readtrigs);
    geo.Save(filename);
  }
}



  class STLGeometryRegister : public GeometryRegister
  {
  public:
    virtual NetgenGeometry * Load (const filesystem::path & filename) const;
  };

  NetgenGeometry *  STLGeometryRegister :: Load (const filesystem::path & filename) const
  {
    string ext = ToLower(filename.extension());

    if (ext == ".stl")
      {
	PrintMessage (1, "Load STL geometry file ", filename);

	ifstream infile(filename);

	STLGeometry * hgeom = STLGeometry :: Load (infile);
	hgeom -> edgesfound = 0;
	return hgeom;
      }
    else if (ext == ".stlb")
      {
	PrintMessage (1, "Load STL binary geometry file ", filename);

	ifstream infile(filename);

	STLGeometry * hgeom = STLGeometry :: LoadBinary (infile);
	hgeom -> edgesfound = 0;
	return hgeom;
      }
    else if (ext == ".nao")
      {
	PrintMessage (1, "Load naomi (F. Kickinger) geometry file ", filename);

	ifstream infile(filename);

	STLGeometry * hgeom = STLGeometry :: LoadNaomi (infile);
	hgeom -> edgesfound = 0;
	return hgeom;
      }

    
    return NULL;
  }


  class STLInit
  {
  public:
    STLInit()
    {
      geometryregister.Append (new STLGeometryRegister);
    }
  };

  STLInit stlinit;

static RegisterClassForArchive<STLGeometry, std::tuple<NetgenGeometry, STLTopology>> stlgeo;
}
