/******************************************************************************* * CGoGN: Combinatorial and Geometric modeling with Generic N-dimensional Maps * * version 0.1 * * Copyright (C) 2009-2012, IGG Team, LSIIT, University of Strasbourg * * * * This library is free software; you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as published by the * * Free Software Foundation; either version 2.1 of the License, or (at your * * option) any later version. * * * * This library is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * * for more details. * * * * You should have received a copy of the GNU Lesser General Public License * * along with this library; if not, write to the Free Software Foundation, * * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * * * Web site: http://cgogn.unistra.fr/ * * Contact information: cgogn@unistra.fr * * * *******************************************************************************/ #include #include "Geometry/bounding_box.h" #include "Geometry/plane_3d.h" #include "Algo/BooleanOperator/mergeVertices.h" #include "Container/fakeAttribute.h" #include namespace CGoGN { namespace Algo { namespace Import { inline bool checkXmlNode(xmlNodePtr node, const std::string& name) { return (strcmp((char*)(node->name),(char*)(name.c_str())) == 0); } template inline bool valueOf(const std::string &s, T &obj) { std::istringstream is(s); return is >> obj; } template bool posSort(const std::pair& a1, const std::pair& a2) { VEC v1 = a1.first; VEC v2 = a2.first; return v1[0] < v2[0] || (v1[0] == v2[0] && v1[1] < v2[1]); } template void getPolygonFromSVG(std::string allcoords, std::vector& curPoly, bool& closedPoly) { closedPoly=false; std::stringstream is(allcoords); bool relative=false; bool push_point; std::string coord; int mode = -1; while ( std::getline( is, coord, ' ' ) ) { float x,y; push_point=false; if(coord[0]=='m' || coord[0]=='l' || coord[0]=='t') //start point, line or quadratic bezier curve { mode = 0; relative=true; } else if(coord[0]=='M' || coord[0] == 'L' || coord[0]=='T') //same in absolute coordinates { mode = 1; relative=false; } else if(coord[0]=='h' || coord[0] == 'H') //horizontal line { mode = 2; relative=(coord[0]=='h'); } else if(coord[0]=='v' || coord[0] == 'V') //vertical line { mode = 3; relative=(coord[0]=='v'); } else if(coord[0]=='c' || coord[0] == 'C') //bezier curve { mode = 4; relative=(coord[0]=='c'); } else if(coord[0]=='s' || coord[0] == 'S' || coord[0]=='q' || coord[0] == 'Q') //bezier curve 2 { mode = 5; relative= ((coord[0]=='s') || (coord[0]=='q')); } else if(coord[0]=='a' || coord[0] == 'A') //elliptic arc { mode =6; relative= (coord[0]=='a'); } else if(coord[0]=='z') //end of path { closedPoly = true; } else //coordinates { switch(mode) { case 0 : //relative break; case 1 : //absolute break; case 2 : //horizontal { std::stringstream streamCoord(coord); std::string xS; std::getline(streamCoord, xS, ',' ); valueOf(xS,x); VEC3 previous = (curPoly)[(curPoly).size()-1]; y = previous[1]; push_point=true; } break; case 3 : //vertical { std::stringstream streamCoord(coord); std::string yS; std::getline(streamCoord, yS, ',' ); valueOf(yS,y); VEC3 previous = (curPoly)[(curPoly).size()-1]; x = previous[0]; push_point=true; } break; case 4 : //bezier { std::getline( is, coord, ' ' ); //ignore first control point std::getline( is, coord, ' ' ); //ignore second control point } break; case 5 : //bezier 2 { std::getline( is, coord, ' ' ); //ignore control point } break; case 6 : //elliptic std::getline( is, coord, ' ' ); //ignore rx std::getline( is, coord, ' ' ); //ignore ry std::getline( is, coord, ' ' ); //ignore x-rotation std::getline( is, coord, ' ' ); //ignore large arc flag std::getline( is, coord, ' ' ); //ignore sweep flag break; } std::stringstream streamCoord(coord); std::string xS,yS; std::getline(streamCoord, xS, ',' ); std::getline(streamCoord, yS, ',' ); valueOf(xS,x); valueOf(yS,y); push_point = true; } //if there is a point to push if(push_point) { VEC3 previous; if(curPoly.size()>0) previous = (curPoly)[(curPoly).size()-1]; if(relative) { x += previous[0]; y += previous[1]; } if(curPoly.size()==0 || (curPoly.size()>0 && (x!=previous[0] || y!= previous[1]))) curPoly.push_back(VEC3(x,y,0)); } } } template void readCoordAndStyle(xmlNode* cur_path, std::vector >& allPoly, std::vector >& allBrokenLines, std::vector& allBrokenLinesWidth) { typedef typename PFP::VEC3 VEC3; typedef std::vector POLYGON; bool closedPoly; POLYGON curPoly; // CGoGNout << "--load a path--"<< CGoGNendl; xmlChar* prop = xmlGetProp(cur_path, BAD_CAST "d"); std::string allcoords((reinterpret_cast(prop))); getPolygonFromSVG(allcoords,curPoly,closedPoly); //check orientation : set in CCW if(curPoly.size()>2) { VEC3 v1(curPoly[1]-curPoly[0]); VEC3 v2(curPoly[2]-curPoly[1]); if((v1^v2)[2]<0) { std::reverse(curPoly.begin(), curPoly.end()); } } //closed polygon ? if(closedPoly) allPoly.push_back(curPoly); else { //if not : read the linewidth for further dilatation allBrokenLines.push_back(curPoly); xmlChar* prop = xmlGetProp(cur_path, BAD_CAST "style"); std::string allstyle((reinterpret_cast(prop))); std::stringstream is(allstyle); std::string style; while ( std::getline( is, style, ';' ) ) { if(style.find("stroke-width:")!=std::string::npos) { std::stringstream isSize(style); std::getline( isSize, style, ':' ); float sizeOfLine; isSize >> sizeOfLine; allBrokenLinesWidth.push_back(sizeOfLine); } } } } template bool importSVG(typename PFP::MAP& map, const std::string& filename, VertexAttribute& position, CellMarker& polygons, CellMarker& polygonsFaces) { typedef typename PFP::VEC3 VEC3; typedef std::vector POLYGON; xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0); xmlNodePtr map_node = xmlDocGetRootElement(doc); if (!checkXmlNode(map_node,"svg")) { CGoGNerr << "Wrong xml format: Root node != svg"<< CGoGNendl; return false; } std::vector allPoly; std::vector allBrokenLines; std::vector allBrokenLinesWidth; for (xmlNode* cur_node = map_node->children; cur_node; cur_node = cur_node->next) { // for each layer if (checkXmlNode(cur_node, "g")) for (xmlNode* cur_path = cur_node->children ; cur_path; cur_path = cur_path->next) { if (checkXmlNode(cur_path, "path")) readCoordAndStyle(cur_path, allPoly, allBrokenLines, allBrokenLinesWidth); } else if (checkXmlNode(cur_node, "path")) readCoordAndStyle(cur_node, allPoly, allBrokenLines, allBrokenLinesWidth); } xmlFreeDoc(doc); Geom::BoundingBox * bb; if(allBrokenLines.size()>0) bb = new Geom::BoundingBox(*(allBrokenLines.begin()->begin())); else if(allPoly.size()>0) bb = new Geom::BoundingBox(*(allPoly.begin()->begin())); else { std::cerr << " no usable data in svg file " << std::endl; return false; } std::cout << "importSVG : XML read." << std::endl; CellMarker brokenMark(map); EdgeAttribute edgeWidth = map.template addAttribute("width"); EdgeAttribute > > edgePlanes = map.template addAttribute >, EDGE>("planes"); ///////////////////////////////////////////////////////////////////////////////////////////// //create broken lines DartMarker brokenL(map); unsigned int nbVertices = 0 ; std::vector::iterator itW = allBrokenLinesWidth.begin(); for(typename std::vector::iterator it = allBrokenLines.begin() ; it != allBrokenLines.end() ; ++it) { if(it->size()<2) { it = allBrokenLines.erase(it); itW = allBrokenLinesWidth.erase(itW); } else { nbVertices += it->size() ; Dart d = map.newFace(it->size()*2-2,false); Dart d1=d; Dart d_1=map.phi_1(d); //build a degenerated "line" face for(unsigned int i = 0; isize() ; ++i) { brokenL.mark(d1); brokenL.mark(d_1); map.sewFaces(d1,d_1,false) ; edgeWidth[d1] = *itW; d1 = map.phi1(d1); d_1 = map.phi_1(d_1); } polygonsFaces.mark(d); //embed the line d1 = d; for(typename POLYGON::iterator emb = it->begin(); emb != it->end() ; emb++) { bb->addPoint(*emb); position[d1] = *emb; d1 = map.phi1(d1); } } itW++; } std::cout << "importSVG : broken lines created : " << nbVertices << " vertices"<< std::endl; ///////////////////////////////////////////////////////////////////////////////////////////// //create polygons // typename std::vector::iterator it; // for(it = allPoly.begin() ; it != allPoly.end() ; ++it) // { // // if(it->size()<4) // { // it = allPoly.erase(it); // } // else // { // Dart d = map.newFace(it->size()-1); //// std::cout << "newFace1 " << it->size()-1 << std::endl; // polygonsFaces.mark(d); // // Dart dd = d; // typename POLYGON::iterator emb = it->begin(); // do // { // bb->addPoint(*emb); // position[dd] = *emb; // emb++; // dd = map.phi1(dd); // } while(dd!=d); // } // } // // for(Dart d = map.begin();d != map.end(); map.next(d)) // { // if(position[d][0] == position[map.phi1(d)][0] && position[d][1] == position[map.phi1(d)][1]) // std::cout << "prob d " << d << std::endl; // } // // DartMarker inside(map); // // for(Dart d = map.begin(); d != map.end(); map.next(d)) // { // polygons.mark(d); // inside.mark(d); // } // // std::cout << "importSVG : Polygons generated." << std::endl; ///////////////////////////////////////////////////////////////////////////////////////////// // DartMarker close(map); // map.closeMap(close); // map.closeMap(); std::cout << "importSVG : Vertices merging..." << std::endl; Algo::BooleanOperator::mergeVertices(map,position); std::cout << "importSVG : Vertices merged." << std::endl; ///////////////////////////////////////////////////////////////////////////////////////////// //cut the edges to have a more regular sampling // float maxDist=60.0f; // CellMarker treated(map,EDGE); // for(Dart d = map.begin(); d != map.end(); map.next(d)) // { // if(!treated.isMarked(d)) // { // treated.mark(d); // VEC3 p1 =position[d]; // VEC3 p2 =position[map.phi1(d)]; // // if((p1-p2).norm()>maxDist) // { // unsigned int nbSeg = ((p1-p2).norm())/int(maxDist); // for(unsigned int i=0;i eMTreated(map); for(Dart d = map.begin();d != map.end(); map.next(d)) { if(brokenL.isMarked(d) && !eMTreated.isMarked(d)) { // -> we convert broken lines to faces to represent their width Dart d1 = d; Dart d2 = map.phi2(d); VEC3 p1 = position[d1]; VEC3 p2 = position[d2]; float width = edgeWidth[d1]/2.0f; if(width==0) std::cout << "importSVG : error width of path is equal to zero" << std::endl; eMTreated.mark(d1); eMTreated.mark(d2); VEC3 v = p2-p1; //take the orthogonal direction to the path to apply width afterward VEC3 ortho = v^VEC3(0,0,1); ortho.normalize(); v.normalize(); //if the valence of one of the vertex is equal to one //cut the edge to insert the quadrangular face // if(map.phi2_1(d1)==d1) if(map.phi_1(d1)==d2) { map.cutEdge(d2); Dart dC = map.phi1(d2); eMTreated.mark(dC); position[map.phi_1(d1)]=p1; edgePlanes[map.phi_1(d1)] = Geom::Plane3D(v,p1); } else { if(d1 != map.phi1(d2) && map.phi_1(d1)!=map.phi1(d2)) { map.splitFace(d1,map.phi1(d2)); } } // if(map.phi2_1(d2)==d2) if(map.phi_1(d2)==d1) { map.cutEdge(d1); Dart dC = map.phi1(d1); eMTreated.mark(dC); position[map.phi_1(d2)]=p2; edgePlanes[map.phi_1(d2)] = Geom::Plane3D(-1.0f*v, p2); } else { if(d2 != map.phi1(d1) && map.phi_1(d2)!=map.phi1(d1)) { map.splitFace(d2,map.phi1(d1)); } } // map.sewFaces(d1, dN); // map.sewFaces(d2, map.phi1(map.phi1(dN))); edgePlanes[d1] = Geom::Plane3D(ortho, p1-(width*ortho)); edgePlanes[d2] = Geom::Plane3D(-1.0f*ortho, p2+(width*ortho)); } } std::cout << "Broken line faces : inserted" << std::endl; // //close the intersections // for(Dart d = map.begin();d != map.end(); map.next(d)) // { // if(map.isBoundaryMarked(map.phi2(d))) // map.closeHole(d); // } //embed the path for(Dart d = map.begin();d != map.end(); map.next(d)) { if(brokenL.isMarked(d)) { Geom::Plane3D pl = edgePlanes[d]; std::cout << "pl " << pl << std::endl; VEC3 pos = position[d]; pl.project(pos); pl = edgePlanes[map.phi_1(d)]; pl.project(pos); position[d] = pos; } } ///////////////////////////////////////////////////////////////////////////////////////////// //process polygons // std::vector > toConnect; // CellMarker connected(map,VERTEX); // for(Dart d = map.begin(); d != map.end(); map.next(d)) // { // if( ((!boundingBox.isMarked(d) && !inside.isMarked(d)) || (boundingBox.isMarked(d) && inside.isMarked(d))) // && !connected.isMarked(d) // ) // { // connected.mark(d); // toConnect.push_back(std::make_pair(position[d],d)); // } // } // // std::sort(toConnect.begin(), toConnect.end(),posSort); // // for(typename std::vector >::iterator it = toConnect.begin(); it != toConnect.end() ; ++it) // { // Dart d = it->second; // typename std::vector >::iterator it2 = it+1; // if(it2!= toConnect.end()) // { // Dart dd = it2->second; // if(!map.sameFace(map.phi2(d),map.phi2(dd))) // { // if(!map.sameFace(dd,d)) { // std::cout << "link" << std::endl; // map.linkVertices(dd,d); // } // else // { // map.splitFace(dd,d); // std::cout << "split" << std::endl; // } // } // } // // } // CellMarker connected(map,VERTEX); // unsigned int i=0; // for(Dart d = map.begin();d != map.end(); map.next(d)) // { // if(!connected.isMarked(d) && !boundingBox.isMarked(d) && !inside.isMarked(d)) // { // i++; // Dart dMin=map.end(); // float distMin = std::numeric_limits::max(); // for(Dart dd = map.begin(); dd != map.end(); map.next(dd)) // { // if( ( // (boundingBox.isMarked(dd) && inside.isMarked(dd)) // || (!boundingBox.isMarked(dd) && !inside.isMarked(dd)) // ) // && !map.sameFace(map.phi2(d),map.phi2(dd))) // { // if(Geom::testOrientation2D(position[dd], position[d], position[map.phi1(d)]) == Geom::LEFT // && Geom::testOrientation2D(position[dd], position[d], position[map.phi_1(d)]) == Geom::RIGHT) // { // float dist = (position[dd]-position[d]).norm(); // if(dist