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)