43     class GeoModelBuilderSVG final : 
public GeoModelBuilderFile< 2 > {
    45         GeoModelBuilderSVG( GeoModel2D& geomodel, 
const std::string& filename )
    46             : GeoModelBuilderFile< 2 >( geomodel, filename ), height_( 0. )
    49         virtual ~GeoModelBuilderSVG() = 
default;
    53             tinyxml2::XMLDocument svg_file;
    54             if( svg_file.LoadFile( filename_.c_str() ) != tinyxml2::XML_SUCCESS ) {
    55                 throw RINGMeshException( 
"I/O", 
"Error while loading svg file." );
    57             tinyxml2::XMLElement* svg_node = svg_file.FirstChildElement( 
"svg" );
    58             if( svg_node == 
nullptr ) {
    59                 throw RINGMeshException( 
"I/O",
    60                     "Error while getting root of svg file." );
    62             height_ = svg_node->DoubleAttribute( 
"height" );
    63             create_lines( svg_node );
    68         void create_lines( tinyxml2::XMLElement* svg_node )
    70             tinyxml2::XMLElement* group = svg_node->FirstChildElement( 
"g" );
    71             while( group != 
nullptr ) {
    72                 vec2 group_translation = get_transform( group );
    73                 tinyxml2::XMLElement* path = group->FirstChildElement( 
"path" );
    74                 while( path != 
nullptr ) {
    75                     vec2 translation = group_translation + get_transform( path );
    76                     std::string data = path->Attribute( 
"d" );
    77                     std::vector< vec2 > vertices = get_path_vertices( data,
    79                     index_t line_id = topology.create_mesh_entity(
    80                         Line2D::type_name_static() ).index();
    81                     geometry.set_line( line_id, vertices );
    82                     path = path->NextSiblingElement( 
"path" );
    84                 group = group->NextSiblingElement( 
"g" );
    88         vec2 get_transform( tinyxml2::XMLElement* group )
    90             const char* attribute = group->Attribute( 
"transform" );
    91             if( attribute == 
nullptr ) {
    94             std::string action, parameters;
    95             GEO::String::split_string( attribute, 
'(', action, parameters );
    96             if( action != 
"translate" ) {
    97                 throw RINGMeshException( 
"I/O", 
"Forbidden transformation ", action,
    98                     " found in the svg file. " );
   100             parameters.pop_back();
   101             std::vector< std::string > coordinates;
   102             GEO::String::split_string( parameters, 
',', coordinates );
   104             translation.x = GEO::String::to_double( coordinates.front() );
   105             translation.y = GEO::String::to_double( coordinates.back() );
   109         void create_corners()
   111             std::vector< vec2 > point_extremities;
   112             point_extremities.reserve( geomodel_.nb_lines() * 2 );
   113             for( 
const auto& line : line_range < 2 > ( geomodel_ ) ) {
   114                 point_extremities.push_back( line.vertex( 0 ) );
   115                 point_extremities.push_back( line.vertex( line.nb_vertices() - 1 ) );
   118             NNSearch2D nn_search( point_extremities );
   119             std::vector< index_t > index_map;
   120             std::vector< vec2 > unique_points;
   121             std::tie( std::ignore, index_map, unique_points ) =
   122                 nn_search.get_colocated_index_mapping_and_unique_points(
   123                     geomodel_.epsilon() );
   125             topology.create_mesh_entities( Corner2D::type_name_static(),
   126                 static_cast< index_t > ( unique_points.size() ) );
   127             for( index_t c : range( geomodel_.nb_corners() ) ) {
   128                 geometry.set_corner( c, unique_points[c] );
   131             for( 
const auto& line : line_range < 2 > ( geomodel_ ) ) {
   132                 gmme_id line_id = line.gmme();
   133                 index_t point0 = index_map[index++ ];
   134                 gmme_id corner0( Corner2D::type_name_static(), point0 );
   135                 index_t point1 = index_map[index++ ];
   136                 gmme_id corner1( Corner2D::type_name_static(), point1 );
   137                 topology.add_mesh_entity_boundary_relation( line_id, corner0 );
   138                 topology.add_mesh_entity_boundary_relation( line_id, corner1 );
   141                 geometry.set_mesh_entity_vertex( line_id, 0, unique_points[point0],
   143                 geometry.set_mesh_entity_vertex( line_id, line.nb_vertices() - 1,
   144                     unique_points[point1], false );
   148         std::vector< vec2 > get_path_vertices(
   150             const vec2& translation )
 const   152             std::vector< std::string > tokens = get_tokens( data );
   153             std::vector< vec2 > vertices;
   154             vertices.reserve( tokens.size() );
   155             bool is_absolute = 
true;
   156             for( index_t i = 0; i < tokens.size(); i++ ) {
   157                 std::string& token = tokens[i];
   158                 if( std::isalpha( token.front() ) ) {
   160                     is_absolute = is_command_absolute( token );
   163                     GEO::String::from_string( token, vertex.x );
   164                     GEO::String::from_string( tokens[++i], vertex.y );
   165                     if( is_absolute || vertices.empty() ) {
   166                         vertex += translation;
   167                         vertex.y = height_ - vertex.y;
   168                         vertices.push_back( vertex );
   170                         vertex.y = -vertex.y;
   171                         vertices.push_back( vertices.back() + vertex );
   178         void throw_if_forbidden_command( 
const std::string& data )
 const   180             std::string forbidden_letters( 
"HhVvCcSs" );
   181             for( 
char letter : forbidden_letters ) {
   182                 if( data.find( letter ) != std::string::npos ) {
   183                     throw RINGMeshException( 
"I/O", 
"Forbidden command ", letter,
   184                         " found in the svg file. ",
   185                         "Flatten your paths before importing in RINGMesh." );
   190         std::vector< std::string > get_tokens( std::string& data )
 const   192             throw_if_forbidden_command( data );
   193             std::replace( data.begin(), data.end(), 
',', 
' ' );
   194             std::string letters( 
"MmLl" );
   195             for( 
char letter : letters ) {
   196                 std::string string_letter = GEO::String::to_string( letter );
   197                 replace_all_string_occurens( data, string_letter,
   198                     " " + string_letter + 
" " );
   200             std::vector< std::string > tokens;
   201             GEO::String::split_string( data, 
' ', tokens );
   205         void replace_all_string_occurens(
   206             std::string& context,
   207             const std::string& to_replace,
   208             const std::string& replacement )
 const   210             std::size_t look_here = 0;
   211             std::size_t found_here;
   212             while( ( found_here = context.find( to_replace, look_here ) )
   213                 != std::string::npos ) {
   214                 context.replace( found_here, to_replace.size(), replacement );
   215                 look_here = found_here + replacement.size();
   219         bool is_command_absolute( 
const std::string& letter )
 const   221             return letter == 
"M" || letter == 
"L";
   228     class SVGIOHandler final: 
public GeoModelIOHandler< 2 > {
   230         virtual ~SVGIOHandler() = 
default;
   232         void load( 
const std::string& filename, GeoModel2D& geomodel ) 
final   234             GeoModelBuilderSVG builder( geomodel, filename );
   235             builder.build_geomodel();
   237         void save( 
const GeoModel2D& geomodel, 
const std::string& filename ) 
final   241             throw RINGMeshException( 
"I/O",
   242                 "Saving a GeoModel in svg not implemented yet" );
 void ringmesh_unused(const T &)
 
#define ringmesh_assert(x)