Forked from
gmsh / gmsh
9475 commits behind the upstream repository.
-
Christophe Geuzaine authoredChristophe Geuzaine authored
CenterlineField.cpp 42.30 KiB
// Gmsh - Copyright (C) 1997-2014 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to the public mailing list <gmsh@geuz.org>.
//
// Contributor(s):
// Emilie Marchandise
//
#include "CenterlineField.h"
#include <vector>
#include <map>
#include <set>
#include <list>
#include <algorithm>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <math.h>
#include "OS.h"
#include "GModel.h"
#include "MElement.h"
#include "MTriangle.h"
#include "MVertex.h"
#include "MLine.h"
#include "StringUtils.h"
#include "GEntity.h"
#include "Field.h"
#include "GFace.h"
#include "discreteEdge.h"
#include "discreteFace.h"
#include "GEdgeCompound.h"
#include "GFaceCompound.h"
#include "BackgroundMesh.h"
#include "meshGFace.h"
#include "meshGEdge.h"
#include "MQuadrangle.h"
#include "Curvature.h"
#include "MElement.h"
#include "Context.h"
#include "directions3D.h"
#include "meshGRegion.h"
#if defined(HAVE_ANN)
#include <ANN/ANN.h>
static void erase(std::vector<MLine*>& lines, MLine* l)
{
std::vector<MLine*>::iterator it = std::remove(lines.begin(), lines.end(), l);
lines.erase(it, lines.end());
}
static double computeLength(std::vector<MLine*> lines)
{
double length= 0.0;
for (unsigned int i = 0; i< lines.size(); i++){
length += lines[i]->getLength();
}
return length;
}
static bool isClosed(std::set<MEdge, Less_Edge> &theCut)
{
std::multiset<MVertex*> boundV;
std::set<MEdge,Less_Edge>::iterator it = theCut.begin();
for (; it!= theCut.end(); it++){
boundV.insert(it->getVertex(0));
boundV.insert(it->getVertex(1));
}
std::multiset<MVertex*>::iterator itb = boundV.begin();
for ( ; itb != boundV.end(); ++itb){
if (boundV.count(*itb) != 2) {
return false;
}
}
return true;
}
static void orderMLines(std::vector<MLine*> &lines, MVertex *vB, MVertex *vE)
{
std::vector<MLine*> _m;
std::list<MLine*> segments;
std::map<MVertex*, MLine*> boundv;
std::vector<int> _orientation;
// store all lines in a list : segments
for (unsigned int i = 0; i < lines.size(); i++){
segments.push_back(lines[i]);
}
// find a lonely MLine
for (std::list<MLine*>::iterator it = segments.begin();
it != segments.end(); ++it){
MVertex *vL = (*it)->getVertex(0);
MVertex *vR = (*it)->getVertex(1);
std::map<MVertex*,MLine*>::iterator it1 = boundv.find(vL);
if (it1 == boundv.end()) boundv.insert(std::make_pair(vL,*it));
else boundv.erase(it1);
std::map<MVertex*,MLine*>::iterator it2 = boundv.find(vR);
if (it2 == boundv.end()) boundv.insert(std::make_pair(vR,*it));
else boundv.erase(it2);
}
// find the first MLine and erase it from the list segments
MLine *firstLine;
if (boundv.size() == 2){ // non periodic
MVertex *v = (boundv.begin())->first;
if ( v == vB) firstLine = (boundv.begin())->second;
else{
MVertex *v = (boundv.rbegin())->first;
if (v == vB) firstLine = (boundv.rbegin())->second;
else{
Msg::Error("begin vertex not found for branch");
return;
}
}
for (std::list<MLine*>::iterator it = segments.begin();
it != segments.end(); ++it){
if (*it == firstLine){
segments.erase(it);
break;
}
}
}
else{
Msg::Error("line is wrong (it has %d end points)", boundv.size());
}
// loop over all segments to order segments and store it in the list _m
_m.push_back(firstLine);
_orientation.push_back(1);
MVertex *first = _m[0]->getVertex(0);
MVertex *last = _m[0]->getVertex(1);
while (first != last){
if (segments.empty())break;
bool found = false;
for (std::list<MLine*>::iterator it = segments.begin();
it != segments.end(); ++it){
MLine *e = *it;
if (e->getVertex(0) == last){
_m.push_back(e);
segments.erase(it);
_orientation.push_back(1);
last = e->getVertex(1);
found = true;
break;
}
else if (e->getVertex(1) == last){
_m.push_back(e);
segments.erase(it);
_orientation.push_back(0);
last = e->getVertex(0);
found = true;
break;
}
}
if (!found && _orientation[0]==1){ //reverse orientation of first Line
if (_m.size() == 1 ){
MVertex *temp = first;
first = last;
last = temp;
_orientation[0] = 0;
}
else {
Msg::Error("lines is wrong");
return;
}
}
}
//lines is now a list of ordered MLines
lines = _m;
//special case reverse orientation
if (lines.size() < 2) return;
if (_orientation[0] && lines[0]->getVertex(1) != lines[1]->getVertex(1)
&& lines[0]->getVertex(1) != lines[1]->getVertex(0)){
for (unsigned int i = 0; i < lines.size(); i++)
_orientation[i] = !_orientation[i];
}
}
static void recurConnectByMEdge(const MEdge &e,
std::multimap<MEdge, MTriangle*, Less_Edge> &e2e,
std::set<MTriangle*> &group,
std::set<MEdge, Less_Edge> &touched,
std::set<MEdge, Less_Edge> &theCut)
{
if (touched.find(e) != touched.end()) return;
touched.insert(e);
for (std::multimap <MEdge, MTriangle*, Less_Edge>::iterator it = e2e.lower_bound(e);
it != e2e.upper_bound(e); ++it){
group.insert(it->second);
for (int i = 0; i < it->second->getNumEdges(); ++i){
MEdge me = it->second->getEdge(i);
if (theCut.find(me) != theCut.end()){
touched.insert(me); //break;
}
else recurConnectByMEdge(me, e2e, group, touched, theCut);
}
}
}
static void cutTriangle(MTriangle *tri,
std::map<MEdge,MVertex*,Less_Edge> &cutEdges,
std::vector<MVertex*> &cutVertices,
std::vector<MTriangle*> &newTris,
std::set<MEdge,Less_Edge> &newCut)
{
MVertex *c[3] = {0,0,0};
for (int j=0;j<3;j++){
MEdge ed = tri->getEdge(j);
std::map<MEdge,MVertex*,Less_Edge> :: iterator it = cutEdges.find(ed);
if (it != cutEdges.end()){
c[j] = it->second;
}
}
MVertex *old_v0 = tri->getVertex(0);
MVertex *old_v1 = tri->getVertex(1);
MVertex *old_v2 = tri->getVertex(2);
if (c[0] && c[1]){
newTris.push_back(new MTriangle (c[0],old_v1,c[1]));
newTris.push_back(new MTriangle (old_v0,c[0],old_v2));
newTris.push_back(new MTriangle (old_v2,c[0],c[1]));
newCut.insert(MEdge(c[0],c[1]));
}
else if (c[0] && c[2]){
newTris.push_back(new MTriangle (old_v0,c[0],c[2]));
newTris.push_back(new MTriangle (c[0],old_v1,old_v2));
newTris.push_back(new MTriangle (old_v2,c[2],c[0]));
newCut.insert(MEdge(c[0],c[2]));
}
else if (c[1] && c[2]){
newTris.push_back(new MTriangle (old_v2,c[2],c[1]));
newTris.push_back(new MTriangle (old_v0,old_v1,c[2]));
newTris.push_back(new MTriangle (c[2],old_v1,c[1]));
newCut.insert(MEdge(c[1],c[2]));
}
else if (c[0]){
newTris.push_back(new MTriangle (old_v0,c[0],old_v2));
newTris.push_back(new MTriangle (old_v2,c[0],old_v1));
if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end()){
newCut.insert(MEdge(c[0],old_v0));
}
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end()) {
newCut.insert(MEdge(c[0],old_v1));
}
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end()){
newCut.insert(MEdge(c[0],old_v2));
}
}
else if (c[1]){
newTris.push_back(new MTriangle (old_v1,c[1],old_v0));
newTris.push_back(new MTriangle (old_v0,c[1],old_v2));
if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end()){
newCut.insert(MEdge(c[1],old_v0));
}
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end()) {
newCut.insert(MEdge(old_v1, c[1]));
}
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end()){
newCut.insert(MEdge(c[1],old_v2));
}
}
else if (c[2]){
newTris.push_back(new MTriangle (old_v0,old_v1, c[2]));
newTris.push_back(new MTriangle (old_v1,old_v2, c[2]));
if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end()){
newCut.insert(MEdge(c[2],old_v0));
}
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end()) {
newCut.insert(MEdge(c[2], old_v1));
}
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end()){
newCut.insert(MEdge(c[2], old_v2));
}
}
else {
newTris.push_back(tri);
if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end() &&
std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end())
newCut.insert(MEdge(old_v0,old_v1));
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end() &&
std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end())
newCut.insert(MEdge(old_v1,old_v2));
else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end() &&
std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end())
newCut.insert(MEdge(old_v2,old_v0));
}
}
Centerline::Centerline(std::string fileName): kdtree(0), kdtreeR(0)
{
recombine = (CTX::instance()->mesh.recombineAll) || (CTX::instance()->mesh.recombine3DAll);
nbPoints = 25;
hLayer = 0.3;
hSecondLayer = 0.3;
nbElemLayer = 3;
nbElemSecondLayer = 0;
is_cut = 0;
is_closed = 0;
is_extruded = 0;
importFile(fileName);
buildKdTree();
update_needed = false;
}
Centerline::Centerline(): kdtree(0), kdtreeR(0)
{
recombine = (CTX::instance()->mesh.recombineAll) || (CTX::instance()->mesh.recombine3DAll);
fileName = "centerlines.vtk";//default
nbPoints = 25;
hLayer = 0.3;
hSecondLayer = 0.3;
nbElemLayer = 3;
nbElemSecondLayer = 0;
is_cut = 0;
is_closed = 0;
is_extruded = 0;
options["closeVolume"] = new FieldOptionInt
(is_closed, "Action: Create In/Outlet planar faces");
options["extrudeWall"] = new FieldOptionInt
(is_extruded, "Action: Extrude wall");
options["reMesh"] = new FieldOptionInt
(is_cut, "Action: Cut the initial mesh in different mesh partitions using the "
"centerlines");
callbacks["run"] = new FieldCallbackGeneric<Centerline>
(this, &Centerline::run, "Run actions (closeVolume, extrudeWall, cutMesh) \n");
options["FileName"] = new FieldOptionString
(fileName, "File name for the centerlines", &update_needed);
options["nbPoints"] = new FieldOptionInt
(nbPoints, "Number of mesh elements in a circle");
options["nbElemLayer"] = new FieldOptionInt
(nbElemLayer, "Number of mesh elements the extruded layer");
options["hLayer"] = new FieldOptionDouble
(hLayer, "Thickness (% of radius) of the extruded layer");
options["nbElemSecondLayer"] = new FieldOptionInt
(nbElemSecondLayer, "Number of mesh elements the second extruded layer");
options["hSecondLayer"] = new FieldOptionDouble
(hSecondLayer, "Thickness (% of radius) of the second extruded layer");
}
Centerline::~Centerline()
{
if (mod) delete mod;
if(kdtree){
ANNpointArray nodes = kdtree->thePoints();
if(nodes) annDeallocPts(nodes);
delete kdtree;
}
if(kdtreeR){
ANNpointArray nodesR = kdtreeR->thePoints();
if(nodesR) annDeallocPts(nodesR);
delete kdtreeR;
}
}
void Centerline::importFile(std::string fileName)
{
current = GModel::current();
std::vector<GFace*> currentFaces(current->firstFace(), current->lastFace());
for (unsigned int i = 0; i < currentFaces.size(); i++){
GFace *gf = currentFaces[i];
if (gf->geomType() == GEntity::DiscreteSurface){
for(unsigned int j = 0; j < gf->triangles.size(); j++)
triangles.push_back(gf->triangles[j]);
if (is_cut){
gf->triangles.clear();
gf->deleteVertexArrays();
current->remove(gf);
}
}
}
if(triangles.empty()){
Msg::Error("Current GModel has no triangles ...");
return;
}
mod = new GModel();
mod->load(fileName);
mod->removeDuplicateMeshVertices(1.e-8);
current->setAsCurrent();
current->setVisibility(1);
int maxN = 0.0;
std::vector<GEdge*> modEdges(mod->firstEdge(), mod->lastEdge());
MVertex *vin = modEdges[0]->lines[0]->getVertex(0);
ptin = SPoint3(vin->x(), vin->y(), vin->z());
for (unsigned int i = 0; i < modEdges.size(); i++){
GEdge *ge = modEdges[i];
for(unsigned int j = 0; j < ge->lines.size(); j++){
MLine *l = ge->lines[j];
MVertex *v0 = l->getVertex(0);
MVertex *v1 = l->getVertex(1);
std::map<MVertex*, int>::iterator it0 = colorp.find(v0);
std::map<MVertex*, int>::iterator it1 = colorp.find(v1);
if (it0 == colorp.end() || it1 == colorp.end()){
lines.push_back(l);
colorl.insert(std::make_pair(l, ge->tag()));
maxN = std::max(maxN, ge->tag());
}
if (it0 == colorp.end()) colorp.insert(std::make_pair(v0, ge->tag()));
if (it1 == colorp.end()) colorp.insert(std::make_pair(v1, ge->tag()));
}
}
createBranches(maxN);
}
void Centerline::createBranches(int maxN)
{
//sort colored lines and create edges
std::vector<std::vector<MLine*> > color_edges;
color_edges.resize(maxN);
std::multiset<MVertex*> allV;
std::map<MLine*, int>::iterator itl = colorl.begin();
while (itl != colorl.end()){
int color = itl->second;
MLine* l = itl->first;
allV.insert(l->getVertex(0));
allV.insert(l->getVertex(1));
color_edges[color-1].push_back(l);
itl++;
}
//detect junctions
std::multiset<MVertex*>::iterator it = allV.begin();
for ( ; it != allV.end(); ++it){
if (allV.count(*it) != 2) {
junctions.insert(*it);
}
}
//split edges
int tag = 0;
for(unsigned int i = 0; i < color_edges.size(); ++i){
std::vector<MLine*> lines = color_edges[i];
while (!lines.empty()) {
std::vector<MLine*> myLines;
std::vector<MLine*>::iterator itl = lines.begin();
MVertex *vB = (*itl)->getVertex(0);
MVertex *vE = (*itl)->getVertex(1);
myLines.push_back(*itl);
erase(lines, *itl);
itl = lines.begin();
while ( !( junctions.find(vE) != junctions.end() &&
junctions.find(vB) != junctions.end()) ) {
MVertex *v1 = (*itl)->getVertex(0);
MVertex *v2 = (*itl)->getVertex(1);
bool goVE = (junctions.find(vE) == junctions.end()) ? true : false ;
bool goVB = (junctions.find(vB) == junctions.end()) ? true : false;
if (v1 == vE && goVE){
myLines.push_back(*itl);
erase(lines, *itl);
itl = lines.begin();
vE = v2;
}
else if ( v2 == vE && goVE){
myLines.push_back(*itl);
erase(lines, *itl);
itl = lines.begin();
vE = v1;
}
else if ( v1 == vB && goVB){
myLines.push_back(*itl);
erase(lines, *itl);
itl = lines.begin();
vB = v2;
}
else if ( v2 == vB && goVB){
myLines.push_back(*itl);
erase(lines, *itl);
itl = lines.begin();
vB = v1;
}
else itl++;
}
if (vB == vE) {
Msg::Error("Begin and end points branch are the same \n");
break;
}
orderMLines(myLines, vB, vE);
std::vector<Branch> children;
Branch newBranch ={ tag++, myLines, computeLength(myLines), vB, vE,
children, 1.e6, 0.0};
edges.push_back(newBranch) ;
}
}
Msg::Info("Centerline: in/outlets =%d branches =%d ",
(int)color_edges.size()+1, (int)edges.size());
//create children
for(unsigned int i = 0; i < edges.size(); ++i) {
MVertex *vE = edges[i].vE;
std::vector<Branch> myChildren;
for (std::vector<Branch>::iterator it = edges.begin(); it != edges.end(); ++it){
Branch myBranch = *it;
if (myBranch.vB == vE) myChildren.push_back(myBranch);
}
edges[i].children = myChildren;
}
//compute radius
distanceToSurface();
computeRadii();
//print for debug
printSplit();
}
void Centerline::distanceToSurface()
{
Msg::Info("Centerline: computing distance to surface mesh ");
//COMPUTE WITH REVERSE ANN TREE (SURFACE POINTS IN TREE)
std::set<MVertex*> allVS;
for(unsigned int j = 0; j < triangles.size(); j++)
for(int k = 0; k<3; k++) allVS.insert(triangles[j]->getVertex(k));
int nbSNodes = allVS.size();
ANNpointArray nodesR = annAllocPts(nbSNodes, 3);
vertices.resize(nbSNodes);
int ind = 0;
std::set<MVertex*>::iterator itp = allVS.begin();
while (itp != allVS.end()){
MVertex *v = *itp;
nodesR[ind][0] = v->x();
nodesR[ind][1] = v->y();
nodesR[ind][2] = v->z();
vertices[ind] = v;
itp++; ind++;
}
kdtreeR = new ANNkd_tree(nodesR, nbSNodes, 3);
for(unsigned int i = 0; i < lines.size(); i++){
MLine *l = lines[i];
MVertex *v1 = l->getVertex(0);
MVertex *v2 = l->getVertex(1);
double midp[3] = {0.5*(v1->x()+v2->x()), 0.5*(v1->y()+v1->y()),0.5*(v1->z()+v2->z())};
ANNidx index[1];
ANNdist dist[1];
kdtreeR->annkSearch(midp, 1, index, dist);
double minRad = sqrt(dist[0]);
radiusl.insert(std::make_pair(lines[i], minRad));
}
}
void Centerline::computeRadii()
{
for(unsigned int i = 0; i < edges.size(); ++i) {
std::vector<MLine*> lines = edges[i].lines;
for(unsigned int j = 0; j < lines.size(); ++j) {
MLine *l = lines[j];
std::map<MLine*,double>::iterator itr = radiusl.find(l);
if (itr != radiusl.end()){
edges[i].minRad = std::min(itr->second, edges[i].minRad);
edges[i].maxRad = std::max(itr->second, edges[i].maxRad);
}
else printf("ARGG line not found \n");
}
}
}
void Centerline::buildKdTree()
{
FILE * f = Fopen("myPOINTS.pos","w");
fprintf(f, "View \"\"{\n");
int nbPL = 3; //10 points per line
//int nbNodes = (lines.size()+1) + (nbPL*lines.size());
int nbNodes = (colorp.size()) + (nbPL*lines.size());
ANNpointArray nodes = annAllocPts(nbNodes, 3);
int ind = 0;
std::map<MVertex*, int>::iterator itp = colorp.begin();
while (itp != colorp.end()){
MVertex *v = itp->first;
nodes[ind][0] = v->x();
nodes[ind][1] = v->y();
nodes[ind][2] = v->z();
itp++; ind++;
}
for(unsigned int k = 0; k < lines.size(); ++k){
MVertex *v0 = lines[k]->getVertex(0);
MVertex *v1 = lines[k]->getVertex(1);
SVector3 P0(v0->x(),v0->y(), v0->z());
SVector3 P1(v1->x(),v1->y(), v1->z());
for (int j= 1; j < nbPL+1; j++){
double inc = (double)j/(double)(nbPL+1);
SVector3 Pj = P0+inc*(P1-P0);
nodes[ind][0] = Pj.x();
nodes[ind][1] = Pj.y();
nodes[ind][2] = Pj.z();
ind++;
}
}
kdtree = new ANNkd_tree(nodes, nbNodes, 3);
for(int i = 0; i < nbNodes; ++i){
fprintf(f, "SP(%g,%g,%g){%g};\n",
nodes[i][0], nodes[i][1],nodes[i][2],1.0);
}
fprintf(f,"};\n");
fclose(f);
}
void Centerline::createSplitCompounds()
{
//number of discrete vertices, edges, faces and regions for the mesh
NV = current->getMaxElementaryNumber(0);
NE = current->getMaxElementaryNumber(1);
NF = current->getMaxElementaryNumber(2);
NR = current->getMaxElementaryNumber(3);
// Remesh new faces (Compound Lines and Compound Surfaces)
Msg::Info("Centerline: creating split compounds ...");
//Parametrize Compound Lines
for (int i=0; i < NE; i++){
std::vector<GEdge*>e_compound;
GEdge *pe = current->getEdgeByTag(i+1);//current edge
e_compound.push_back(pe);
int num_gec = NE+i+1;
Msg::Info("Create Compound Line (%d) = %d discrete edge",
num_gec, pe->tag());
GEdge *gec = current->addCompoundEdge(e_compound,num_gec);
if (CTX::instance()->mesh.algo2d != ALGO_2D_BAMG){
gec->meshAttributes.method = MESH_TRANSFINITE;
gec->meshAttributes.nbPointsTransfinite = nbPoints+1;
gec->meshAttributes.typeTransfinite = 0;
gec->meshAttributes.coeffTransfinite = 1.0;
}
}
// Parametrize Compound surfaces
std::list<GEdge*> U0;
for (int i=0; i < NF; i++){
std::vector<GFace*> f_compound;
GFace *pf = current->getFaceByTag(i+1);//current face
f_compound.push_back(pf);
int num_gfc = NF+i+1;
Msg::Info("Create Compound Surface (%d) = %d discrete face",
num_gfc, pf->tag());
//1=conf_spectral 4=convex_circle, 7=conf_fe
GFace *gfc = current->addCompoundFace(f_compound, 7, 0, num_gfc);
gfc->meshAttributes.recombine = recombine;
gfc->addPhysicalEntity(1);
current->setPhysicalName("wall", 2, 1);//tag 1
}
}
void Centerline::createFaces()
{
std::vector<std::vector<MTriangle*> > faces;
std::multimap<MEdge, MTriangle*, Less_Edge> e2e;
for(unsigned int i = 0; i < triangles.size(); ++i)
for(int j = 0; j < 3; j++)
e2e.insert(std::make_pair(triangles[i]->getEdge(j), triangles[i]));
while(!e2e.empty()){
std::set<MTriangle*> group;
std::set<MEdge, Less_Edge> touched;
group.clear();
touched.clear();
std::multimap<MEdge, MTriangle*, Less_Edge>::iterator ite = e2e.begin();
MEdge me = ite->first;
while (theCut.find(me) != theCut.end()){
ite++;
me = ite->first;
}
recurConnectByMEdge(me,e2e, group, touched, theCut);
std::vector<MTriangle*> temp;
temp.insert(temp.begin(), group.begin(), group.end());
faces.push_back(temp);
for(std::set<MEdge, Less_Edge>::iterator it = touched.begin();
it != touched.end(); ++it)
e2e.erase(*it);
}
Msg::Info("Centerline: action (cutMesh) has cut surface mesh in %d faces ",
(int)faces.size());
//create discFaces
for(unsigned int i = 0; i < faces.size(); ++i){
int numF = current->getMaxElementaryNumber(2) + 1;
discreteFace *f = new discreteFace(current, numF);
current->add(f);
discFaces.push_back(f);
std::set<MVertex*> myVertices;
std::vector<MTriangle*> myFace = faces[i];
for(unsigned int j= 0; j< myFace.size(); j++){
MTriangle *t = myFace[j];
f->triangles.push_back(t);
for (int k= 0; k< 3; k++){
MVertex *v = t->getVertex(k);
myVertices.insert(v);
v->setEntity(f);
}
}
f->mesh_vertices.insert(f->mesh_vertices.begin(),
myVertices.begin(), myVertices.end());
}
}
void Centerline::createClosedVolume(GEdge *gin, std::vector<GEdge*> boundEdges)
{
current->setFactory("Gmsh");
std::vector<std::vector<GFace *> > myFaceLoops;
std::vector<GFace *> myFaces;
for (unsigned int i = 0; i< boundEdges.size(); i++){
std::vector<std::vector<GEdge *> > myEdgeLoops;
std::vector<GEdge *> myEdges;
GEdge * gec;
if(is_cut) gec = current->getEdgeByTag(NE+boundEdges[i]->tag());
else gec = current->getEdgeByTag(boundEdges[i]->tag());
myEdges.push_back(gec);
myEdgeLoops.push_back(myEdges);
GFace *newFace = current->addPlanarFace(myEdgeLoops);
if (gin==boundEdges[i]) {
newFace->addPhysicalEntity(2);
current->setPhysicalName("inlet", 2, 2);//tag 2
}
else{
newFace->addPhysicalEntity(3);
current->setPhysicalName("outlets", 2, 3);//tag 3
}
myFaces.push_back(newFace);
}
Msg::Info("Centerline: action (closeVolume) has created %d in/out planar faces ",
(int)boundEdges.size());
for (int i = 0; i < NF; i++){
GFace * gf;
if(is_cut) gf = current->getFaceByTag(NF+i+1);
else gf = current->getFaceByTag(i+1);
myFaces.push_back(gf);
}
myFaceLoops.push_back(myFaces);
GRegion *reg = current->addVolume(myFaceLoops);
reg->addPhysicalEntity(reg->tag());
current->setPhysicalName("lumenVolume", 3, reg->tag());
Msg::Info("Centerline: action (closeVolume) has created volume %d ", reg->tag());
}
void Centerline::extrudeBoundaryLayerWall(GEdge* gin, std::vector<GEdge*> boundEdges)
{
Msg::Info("Centerline: extrude boundary layer wall (%d, %g%%R) ", nbElemLayer, hLayer);
//orient extrude direction outward
int dir = 0;
MElement *e = current->getFaceByTag(1)->getMeshElement(0);
SVector3 ne = e->getFace(0).normal();
SVector3 ps(e->getVertex(0)->x(), e->getVertex(0)->y(), e->getVertex(0)->z());
double xyz[3] = {ps.x(), ps.y(), ps.z()};
ANNidx index[1];
ANNdist dist[1];
kdtree->annkSearch(xyz, 1, index, dist);
ANNpointArray nodes = kdtree->thePoints();
SVector3 pc(nodes[index[0]][0], nodes[index[0]][1], nodes[index[0]][2]);
SVector3 nc = ps-pc;
if (dot(ne,nc) < 0) dir = 1;
if (dir == 1 && hLayer > 0 ) hLayer *= -1.0;
//int shift = 0;
//if(is_cut) shift = NE;
for (int i= 0; i< NF; i++){
GFace *gfc ;
if (is_cut) gfc = current->getFaceByTag(NF+i+1);
else gfc = current->getFaceByTag(i+1);
current->setFactory("Gmsh");
//view -5 to scale hLayer by radius in BoundaryLayers.cpp
std::vector<GEntity*> extrudedE = current->extrudeBoundaryLayer
(gfc, nbElemLayer, hLayer, dir, -5);
GFace *eFace = (GFace*) extrudedE[0];
eFace->addPhysicalEntity(5);
current->setPhysicalName("outerWall", 2, 5);//dim 2 tag 5
GRegion *eRegion = (GRegion*) extrudedE[1];
eRegion->addPhysicalEntity(6);
current->setPhysicalName("wallVolume", 3, 6);//dim 3 tag 6
//if double extruded layer
if (nbElemSecondLayer > 0){
std::vector<GEntity*> extrudedESec = current->extrudeBoundaryLayer
(eFace, nbElemSecondLayer, hSecondLayer, dir, -5);
GFace *eFaceSec = (GFace*) extrudedESec[0];
eFaceSec->addPhysicalEntity(9); //tag 9
current->setPhysicalName("outerSecondWall", 2, 9);//dim 2 tag 9
GRegion *eRegionSec = (GRegion*) extrudedESec[1];
eRegionSec->addPhysicalEntity(10); //tag 10
current->setPhysicalName("wallVolume", 3, 10);//dim 3 tag 10
}
//end double extrusion
for (unsigned int j = 2; j < extrudedE.size(); j++){
GFace *elFace = (GFace*) extrudedE[j];
std::list<GEdge*> l_edges = elFace->edges();
for(std::list<GEdge*>::iterator it = l_edges.begin(); it != l_edges.end(); it++){
GEdge *myEdge = *it;
if (is_cut) myEdge = current->getEdgeByTag((*it)->tag()-NE);
if( std::find(boundEdges.begin(), boundEdges.end(), myEdge) != boundEdges.end() ){
if (myEdge==gin){
elFace->addPhysicalEntity(7);
current->setPhysicalName("inletRing", 2, 7);//tag 7
}
else{
elFace->addPhysicalEntity(8);
current->setPhysicalName("outletRings", 2, 8);//tag 8
}
}
}
}
}
}
void Centerline::run()
{
double t1 = Cpu();
if (update_needed){
std::ifstream input;
//std::string pattern = FixRelativePath(fileName, "./");
//Msg::StatusBar(true, "Reading TEST '%s'...", pattern.c_str());
//input.open(pattern.c_str());
input.open(fileName.c_str());
if(StatFile(fileName))
Msg::Fatal("Centerline file '%s' does not exist ", fileName.c_str());
importFile(fileName);
buildKdTree();
update_needed = false;
}
if (is_cut) cutMesh();
else{
GFace *gf = current->getFaceByTag(1);
gf->addPhysicalEntity(1);
current->setPhysicalName("wall", 2, 1);//tag 1
current->createTopologyFromMesh();
NV = current->getMaxElementaryNumber(0);
NE = current->getMaxElementaryNumber(1);
NF = current->getMaxElementaryNumber(2);
NR = current->getMaxElementaryNumber(3);
}
//identify the boundary edges by looping over all discreteFaces
std::vector<GEdge*> boundEdges;
double dist_inlet = 1.e6;
GEdge *gin = NULL;
for (int i= 0; i< NF; i++){
GFace *gf = current->getFaceByTag(i+1);
std::list<GEdge*> l_edges = gf->edges();
for(std::list<GEdge*>::iterator it = l_edges.begin(); it != l_edges.end(); it++){
std::vector<GEdge*>::iterator ite = std::find(boundEdges.begin(),
boundEdges.end(), *it);
if (ite != boundEdges.end()) boundEdges.erase(ite);
else boundEdges.push_back(*it);
GVertex *gv = (*it)->getBeginVertex();
SPoint3 pt(gv->x(), gv->y(), gv->z());
double dist = pt.distance(ptin);
if(dist < dist_inlet){
dist_inlet = dist;
gin = *it;
}
}
}
if (is_closed) createClosedVolume(gin, boundEdges);
if (is_extruded) extrudeBoundaryLayerWall(gin, boundEdges);
double t2 = Cpu();
Msg::Info("Centerline operators computed in %g (s) ",t2-t1);
}
void Centerline::cutMesh()
{
Msg::Info("Centerline: action (cutMesh) splits surface mesh (%d tris) using %s ",
triangles.size(), fileName.c_str());
//splitMesh
for(unsigned int i = 0; i < edges.size(); i++){
std::vector<MLine*> lines = edges[i].lines;
double L = edges[i].length;
double D = 2.*edges[i].minRad; //(edges[i].minRad+edges[i].maxRad);
double AR = L/D;
// printf("*** Centerline branch %d (AR=%.1f) \n", edges[i].tag, AR);
int nbSplit = (int)ceil(AR/2 + 1.1); //AR/2 + 0.9
if( nbSplit > 1 ){
//printf("->> cut branch in %d parts \n", nbSplit);
double li = L/nbSplit;
double lc = 0.0;
for (unsigned int j= 0; j < lines.size(); j++){
lc += lines[j]->getLength();
if (lc > li && nbSplit > 1) {
MVertex *v1 = lines[j]->getVertex(0);
MVertex *v2 = lines[j]->getVertex(1);
SVector3 pt(v1->x(), v1->y(), v1->z());
SVector3 dir(v2->x()-v1->x(),v2->y()-v1->y(),v2->z()-v1->z());
std::map<MLine*,double>::iterator itr = radiusl.find(lines[j]);
cutByDisk(pt, dir, itr->second);
nbSplit--;
lc = 0.0;
}
}
}
if(edges[i].children.size() > 0.0 && AR > 1.0){
MVertex *v1 = lines[lines.size()-1]->getVertex(1);//end vertex
MVertex *v2;
if(AR < 1.5) v2 = lines[0]->getVertex(0);
else if (lines.size() > 4) v2 = lines[lines.size()-4]->getVertex(0);
else v2 = lines[lines.size()-1]->getVertex(0);
SVector3 pt(v1->x(), v1->y(), v1->z());
SVector3 dir(v2->x()-v1->x(),v2->y()-v1->y(),v2->z()-v1->z());
//printf("-->> cut branch at bifurcation \n");
std::map<MLine*,double>::iterator itr = radiusl.find(lines[lines.size()-1]);
//bool cutted =
cutByDisk(pt, dir, itr->second);
// if(!cutted){
// int l = lines.size()-1-lines.size()/(4*nbSplit); //chech this!
// v1 = lines[l]->getVertex(1);
// v2 = lines[l]->getVertex(0);
// pt = SVector3(v1->x(), v1->y(), v1->z());
// dir = SVector3(v2->x()-v1->x(),v2->y()-v1->y(),v2->z()-v1->z());
// printf("-->> cut bifurcation NEW \n");
// itr = radiusl.find(lines[l]);
// cutted = cutByDisk(pt, dir, itr->second);
// }
}
}
//create discreteFaces
createFaces();
current->createTopologyFromFaces(discFaces);
current->exportDiscreteGEOInternals();
//write
Msg::Info("Centerline: writing splitted mesh 'myPARTS.msh'");
current->writeMSH("myPARTS.msh", 2.2, false, false);
//create compounds
createSplitCompounds();
Msg::Info("Done splitting mesh by centerlines");
}
bool Centerline::cutByDisk(SVector3 &PT, SVector3 &NORM, double &maxRad)
{
double a = NORM.x();
double b = NORM.y();
double c = NORM.z();
double d = -a * PT.x() - b * PT.y() - c * PT.z();
int maxStep = 20;
const double EPS = 0.007;
//std::set<MEdge,Less_Edge> allEdges;
std::vector<MEdge> allEdges;
for(unsigned int i = 0; i < triangles.size(); i++){
for ( unsigned int j= 0; j < 3; j++){
allEdges.push_back(triangles[i]->getEdge(j));
//allEdges.insert(triangles[i]->getEdge(j));
}
}
std::unique(allEdges.begin(), allEdges.end());
bool closedCut = false;
int step = 0;
while (!closedCut && step < maxStep){
double rad = 1.1*maxRad+0.05*step*maxRad;
std::map<MEdge,MVertex*,Less_Edge> cutEdges;
std::vector<MVertex*> cutVertices;
std::vector<MTriangle*> newTris;
std::set<MEdge,Less_Edge> newCut;
cutEdges.clear();
cutVertices.clear();
newTris.clear();
newCut.clear();
// for (std::set<MEdge,Less_Edge>::iterator it = allEdges.begin();
// it != allEdges.end() ; ++it){
// MEdge me = *it;
for (unsigned int j = 0; j < allEdges.size(); j++){
MEdge me = allEdges[j];
SVector3 P1(me.getVertex(0)->x(),me.getVertex(0)->y(), me.getVertex(0)->z());
SVector3 P2(me.getVertex(1)->x(),me.getVertex(1)->y(), me.getVertex(1)->z());
double V1 = a * P1.x() + b * P1.y() + c * P1.z() + d;
double V2 = a * P2.x() + b * P2.y() + c * P2.z() + d;
bool inters = (V1*V2<=0.0) ? true: false;
bool inDisk = ((norm(P1-PT) < rad ) || (norm(P2-PT) < rad)) ? true : false;
double rdist = -V1/(V2-V1);
if (inters && rdist > EPS && rdist < 1.-EPS){
SVector3 PZ = P1+rdist*(P2-P1);
if (inDisk){
MVertex *newv = new MVertex (PZ.x(), PZ.y(), PZ.z());
cutEdges.insert(std::make_pair(me,newv));
}
}
else if (inters && rdist <= EPS && inDisk )
cutVertices.push_back(me.getVertex(0));
else if (inters && rdist >= 1.-EPS && inDisk)
cutVertices.push_back(me.getVertex(1));
}
for(unsigned int i = 0; i < triangles.size(); i++){
cutTriangle(triangles[i], cutEdges,cutVertices, newTris, newCut);
}
if (isClosed(newCut)) {
triangles.clear();
triangles = newTris;
theCut.insert(newCut.begin(),newCut.end());
break;
}
else {
step++;
//if (step == maxStep) {printf("no closed cut %d \n", (int)newCut.size()); };
// // triangles = newTris;
// // theCut.insert(newCut.begin(),newCut.end());
// char name[256];
// sprintf(name, "myCUT-%d.pos", step);
// FILE * f2 = Fopen(name,"w");
// fprintf(f2, "View \"\"{\n");
// std::set<MEdge,Less_Edge>::iterator itp = newCut.begin();
// while (itp != newCut.end()){
// MEdge l = *itp;
// fprintf(f2, "SL(%g,%g,%g,%g,%g,%g){%g,%g};\n",
// l.getVertex(0)->x(), l.getVertex(0)->y(), l.getVertex(0)->z(),
// l.getVertex(1)->x(), l.getVertex(1)->y(), l.getVertex(1)->z(),
// 1.0,1.0);
// itp++;
// }
// fprintf(f2,"};\n");
// fclose(f2);
}
}
if (step < maxStep){
//printf("cutByDisk OK step =%d \n", step);
return true;
}
else {
//printf("cutByDisk not succeeded \n");
return false;
}
}
double Centerline::operator() (double x, double y, double z, GEntity *ge)
{
if (update_needed){
std::ifstream input;
input.open(fileName.c_str());
if(StatFile(fileName))
Msg::Fatal("Centerline file '%s' does not exist", fileName.c_str());
importFile(fileName);
buildKdTree();
update_needed = false;
}
double xyz[3] = {x,y,z};
//take xyz = closest point on boundary in case we are on the planar in/out faces
//or in the volume
bool isCompound = false;
if(ge){
if (ge->dim() == 2 && ge->geomType() == GEntity::CompoundSurface) isCompound = true;
std::list<GFace*> cFaces;
if (isCompound) cFaces = ((GFaceCompound*)ge)->getCompounds();
if ( ge->dim() == 3 || (ge->dim() == 2 && ge->geomType() == GEntity::Plane) ||
(isCompound && (*cFaces.begin())->geomType() == GEntity::Plane) ){
const int num_neighbours = 1;
ANNidx index[num_neighbours];
ANNdist dist[num_neighbours];
kdtreeR->annkSearch(xyz, num_neighbours, index, dist);
ANNpointArray nodesR = kdtreeR->thePoints();
xyz[0] = nodesR[index[0]][0];
xyz[1] = nodesR[index[0]][1];
xyz[2] = nodesR[index[0]][2];
}
}
const int num_neighbours = 1;
ANNidx index[num_neighbours];
ANNdist dist[num_neighbours];
kdtree->annkSearch(xyz, num_neighbours, index, dist);
double rad = sqrt(dist[0]);
//double cmax, cmin;
//SVector3 dirMax,dirMin;
//cmax = ge->curvatures(SPoint2(u, v),&dirMax, &dirMin, &cmax,&cmin);
//cmax = ge->curvatureMax(SPoint2(u,v));
//double radC = 1./cmax;
double lc = 2*M_PI*rad/nbPoints;
if(!ge) { return rad;}
else return lc;
}
void Centerline::operator() (double x, double y, double z, SMetric3 &metr, GEntity *ge)
{
if (update_needed){
std::ifstream input;
input.open(fileName.c_str());
if(StatFile(fileName))
Msg::Fatal("Centerline file '%s' does not exist", fileName.c_str());
importFile(fileName);
buildKdTree();
update_needed = false;
}
//take xyz = closest point on boundary in case we are on
//the planar IN/OUT FACES or in VOLUME
double xyz[3] = {x,y,z};
bool onTubularSurface = true;
double ds = 0.0;
bool isCompound = (ge->dim() == 2 && ge->geomType() == GEntity::CompoundSurface) ?
true : false;
bool onInOutlets = (ge->geomType() == GEntity::Plane) ? true: false;
std::list<GFace*> cFaces;
if (isCompound) cFaces = ((GFaceCompound*)ge)->getCompounds();
if ( ge->dim() == 3 || (ge->dim() == 2 && ge->geomType() == GEntity::Plane) ||
(isCompound && (*cFaces.begin())->geomType() == GEntity::Plane) ){
onTubularSurface = false;
}
ANNidx index[1];
ANNdist dist[1];
kdtreeR->annkSearch(xyz, 1, index, dist);
if (! onTubularSurface){
ANNpointArray nodesR = kdtreeR->thePoints();
ds = sqrt(dist[0]);
xyz[0] = nodesR[index[0]][0];
xyz[1] = nodesR[index[0]][1];
xyz[2] = nodesR[index[0]][2];
}
ANNidx index2[2];
ANNdist dist2[2];
kdtree->annkSearch(xyz, 2, index2, dist2);
double radMax = sqrt(dist2[0]);
ANNpointArray nodes = kdtree->thePoints();
SVector3 p0(nodes[index2[0]][0], nodes[index2[0]][1], nodes[index2[0]][2]);
SVector3 p1(nodes[index2[1]][0], nodes[index2[1]][1], nodes[index2[1]][2]);
//dir_a = direction along the centerline
//dir_n = normal direction of the disk
//dir_t = tangential direction of the disk
SVector3 dir_a = p1-p0; dir_a.normalize();
SVector3 dir_n(xyz[0]-p0.x(), xyz[1]-p0.y(), xyz[2]-p0.z()); dir_n.normalize();
SVector3 dir_cross = crossprod(dir_a,dir_n); dir_cross.normalize();
// if (ge->dim()==3){
// printf("coucou dim ==3 !!!!!!!!!!!!!!! \n");
// SVector3 d1,d2,d3;
// computeCrossField(x,y,z,d1,d2,d3);
// exit(1);
// }
//find discrete curvature at closest vertex
Curvature& curvature = Curvature::getInstance();
if( !Curvature::valueAlreadyComputed() ) {
Msg::Info("Need to compute discrete curvature");
Curvature::typeOfCurvature type = Curvature::RUSIN;
curvature.computeCurvature(current, type);
}
double curv, cMin, cMax;
SVector3 dMin, dMax;
int isAbs = 1.0;
curvature.vertexNodalValuesAndDirections(vertices[index[0]],&dMax, &dMin, &cMax, &cMin, isAbs);
curvature.vertexNodalValues(vertices[index[0]], curv, 1);
if (cMin == 0) cMin =1.e-12;
if (cMax == 0) cMax =1.e-12;
double rhoMin = 1./cMin;
double rhoMax = 1./cMax;
//double signMin = (rhoMin > 0.0) ? -1.0: 1.0;
//double signMax = (rhoMax > 0.0) ? -1.0: 1.0;
//user-defined parameters
//define h_n, h_t1, and h_t2
double thickness = radMax/3.;
double h_far = radMax/5.;
double beta = (ds <= thickness) ? 1.2 : 2.1; //CTX::instance()->mesh.smoothRatio;
double ddist = (ds <= thickness) ? ds: thickness;
double h_n_0 = thickness/20.;
double h_n = std::min( (h_n_0+ds*log(beta)), h_far);
double betaMin = 10.0;
double betaMax = 3.1;
double oneOverD2_min = 1./(2.*rhoMin*rhoMin*(betaMin*betaMin-1)) *
(sqrt(1+ (4.*rhoMin*rhoMin*(betaMin*betaMin-1))/(h_n*h_n))-1.);
double oneOverD2_max = 1./(2.*rhoMax*rhoMax*(betaMax*betaMax-1)) *
(sqrt(1+ (4.*rhoMax*rhoMax*(betaMax*betaMax-1))/(h_n*h_n))-1.);
double h_t1_0 = sqrt(1./oneOverD2_min);
double h_t2_0 = sqrt(1./oneOverD2_max);
//double h_t1 = h_t1_0*(rhoMin+signMin*ddist)/rhoMin ;
//double h_t2 = h_t2_0*(rhoMax+signMax*ddist)/rhoMax ;
double h_t1 = std::min( (h_t1_0+(ddist*log(beta))), radMax);
double h_t2 = std::min( (h_t2_0+(ddist*log(beta))), h_far);
double dCenter = radMax-ds;
double h_a_0 = 0.5*radMax;
double h_a = h_a_0 - (h_a_0-h_t1_0)/(radMax)*dCenter;
//length between min and max
double lcMin = ((2 * M_PI *radMax) /( 50*nbPoints )); //CTX::instance()->mesh.lcMin;
double lcMax = lcMin*2000.; //CTX::instance()->mesh.lcMax;
h_n = std::max(h_n, lcMin); h_n = std::min(h_n, lcMax);
h_t1 = std::max(h_t1, lcMin); h_t1 = std::min(h_t1, lcMax);
h_t2 = std::max(h_t2, lcMin); h_t2 = std::min(h_t2, lcMax);
//curvature metric
SMetric3 curvMetric, curvMetric1, curvMetric2;
SMetric3 centMetric1, centMetric2, centMetric;
if (onInOutlets){
metr = buildMetricTangentToCurve(dir_n,h_n,h_t2);
}
else {
//on surface and in volume boundary layer
if ( ds < thickness ){
metr = metricBasedOnSurfaceCurvature(dMin, dMax, cMin, cMax, h_n, h_t1, h_t2);
}
//in volume
else {
//curvMetric = metricBasedOnSurfaceCurvature(dMin, dMax, cMin, cMax, h_n, h_t1, h_t2);
metr = SMetric3( 1./(h_a*h_a), 1./(h_n*h_n), 1./(h_n*h_n), dir_a, dir_n, dir_cross);
//metr = intersection_conserveM1_bis(metr, curvMetric);
//metr = intersection_conserveM1(metr,curvMetric);
//metr = intersection_conserve_mostaniso(metr, curvMetric);
//metr = intersection(metr,curvMetric);
}
}
return;
}
SMetric3 Centerline::metricBasedOnSurfaceCurvature(SVector3 dirMin, SVector3 dirMax,
double cmin, double cmax,
double h_n, double h_t1, double h_t2)
{
SVector3 dirNorm = crossprod(dirMax,dirMin);
SMetric3 curvMetric (1./(h_t1*h_t1),1./(h_t2*h_t2),1./(h_n*h_n), dirMin, dirMax, dirNorm);
return curvMetric;
}
void Centerline::computeCrossField(double x,double y,double z,
SVector3 &d1, SVector3 &d2, SVector3 &d3)
{
//Le code suivant permet d'obtenir les trois vecteurs orthogonaux en un point.
// int NumSmooth = 10;//CTX::instance()->mesh.smoothCrossField
// Matrix m2;
// if(NumSmooth)
// m2 = Frame_field::findCross(x,y,z);
// else
// m2 = Frame_field::search(x,y,z);
}
void Centerline::printSplit() const
{
FILE * f = Fopen("mySPLIT.pos","w");
fprintf(f, "View \"\"{\n");
for(unsigned int i = 0; i < edges.size(); ++i){
std::vector<MLine*> lines = edges[i].lines;
for(unsigned int k = 0; k < lines.size(); ++k){
MLine *l = lines[k];
fprintf(f, "SL(%g,%g,%g,%g,%g,%g){%g,%g};\n",
l->getVertex(0)->x(), l->getVertex(0)->y(), l->getVertex(0)->z(),
l->getVertex(1)->x(), l->getVertex(1)->y(), l->getVertex(1)->z(),
(double)edges[i].tag, (double)edges[i].tag);
}
}
fprintf(f,"};\n");
fclose(f);
// FILE * f3 = Fopen("myJUNCTIONS.pos","w");
// fprintf(f3, "View \"\"{\n");
// std::set<MVertex*>::const_iterator itj = junctions.begin();
// while (itj != junctions.end()){
// MVertex *v = *itj;
// fprintf(f3, "SP(%g,%g,%g){%g};\n",
// v->x(), v->y(), v->z(),
// (double)v->getNum());
// itj++;
// }
// fprintf(f3,"};\n");
// fclose(f3);
FILE * f4 = Fopen("myRADII.pos","w");
fprintf(f4, "View \"\"{\n");
for(unsigned int i = 0; i < lines.size(); ++i){
MLine *l = lines[i];
std::map<MLine*,double>::const_iterator itc = radiusl.find(l);
fprintf(f4, "SL(%g,%g,%g,%g,%g,%g){%g,%g};\n",
l->getVertex(0)->x(), l->getVertex(0)->y(), l->getVertex(0)->z(),
l->getVertex(1)->x(), l->getVertex(1)->y(), l->getVertex(1)->z(),
itc->second,itc->second);
}
fprintf(f4,"};\n");
fclose(f4);
}
#endif