1 /*
2 This file is a part of the RepSnapper project.
3 Copyright (C) 2012 martin.dieringer@gmx.de
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "flatshape.h"
21 #include "ui/progress.h"
22 // #include "settings.h"
23 #include "clipping.h"
24
25 #ifdef _OPENMP
26 #include <omp.h>
27 #endif
28
29
FlatShape()30 FlatShape::FlatShape()
31 {
32 slow_drawing = false;
33 Min.set(0,0,0);
34 Max.set(200,200,0);
35 CalcBBox();
36 }
37
38
FlatShape(string filename)39 FlatShape::FlatShape(string filename)
40 {
41 slow_drawing = false;
42 this->filename = filename;
43 loadSVG(filename);
44 }
45
46 // FlatShape::FlatShape(const FlatShape &rhs)
47 // {
48 // slow_drawing = false;
49 // polygons = rhs.polygons;
50 // scale_factor_x = rhs.scale_factor_x;
51 // scale_factor_y = rhs.scale_factor_y;
52 // scale_factor = rhs.scale_factor;
53 // Min = rhs.Min;
54 // Max = rhs.Max;
55 // Center = rhs.Center;
56 // }
57
REMatches(const Glib::RefPtr<Glib::Regex> & RE,const string & input,const string & name)58 vector<string> REMatches(const Glib::RefPtr<Glib::Regex> &RE,
59 const string &input,
60 const string &name)
61 {
62 Glib::MatchInfo match_info;
63 vector<string> matches;
64 if (RE->match(input, match_info)) {
65 matches.push_back(match_info.fetch_named(name).c_str());
66 while (match_info.next()){
67 matches.push_back(match_info.fetch_named(name).c_str());
68 }
69 }
70 return matches;
71 }
REMatches(const string & regex,const string & input,const string & name)72 vector<string> REMatches(const string ®ex,
73 const string &input,
74 const string &name)
75 {
76 Glib::RefPtr<Glib::Regex> RE = Glib::Regex::create(regex);
77 return REMatches(RE, input,name);
78 }
79
80 // int loadSVGold(string filename)
81 // {
82 // Min=Vector3d(1000000,1000000,0);
83 // Max=Vector3d(-1000000,-1000000,0);
84
85 // polygons.clear();
86
87 // ifstream file;
88 // file.open(filename.c_str());
89
90
91 // string lines;
92 // string line;
93 // if (file.is_open()) {
94 // while ( file.good() ) {
95 // getline (file,line);
96 // lines += line;
97 // }
98 // file.close();
99
100 // Glib::RefPtr<Glib::Regex> polyregex =
101 // Glib::Regex::create ("(?ims)<path.*?stroke\\:none.*?\\sd\\=\"(?<POLY>.*?(Z|\"/\\>))");
102 // Glib::RefPtr<Glib::Regex> strokeregex =
103 // Glib::Regex::create ("(?ims)<path.*?(?<STROKE>fill\\:none.*?\\sd\\=\".*?\"/\\>)");
104 // Glib::RefPtr<Glib::Regex> strwidthregex =
105 // Glib::Regex::create ("stroke\\-width\\:(?<STRWIDTH>[\\-\\.\\d]+)");
106 // Glib::RefPtr<Glib::Regex> pointregex =
107 // Glib::Regex::create ("(?<POINT>[LM](\\s+[\\-\\.\\d]+){2})");
108 // Glib::RefPtr<Glib::Regex> transregex =
109 // Glib::Regex::create ("transform=\"(?<TRANS>.*?)\"");
110 // Glib::RefPtr<Glib::Regex> matrixregex =
111 // Glib::Regex::create ("matrix\\((?<MATR>([\\-\\.\\d]*?(\\,|\\))){6})");
112
113 // vector<string> strokes = REMatches(strokeregex, lines, "STROKE");
114 // for (uint i = 0; i < strokes.size(); i++) {
115 // cerr << i << ": "<<strokes[i] << endl;
116 // vector<string> strwidth = REMatches(strwidthregex,strokes[i],"STRWIDTH");
117 // for (uint j = 0; j < strwidth.size(); j++) {
118 // cerr << "STRW " << strwidth[j] << endl;
119 // }
120 // vector<string> points = REMatches(pointregex,strokes[i],"POINT");
121 // for (uint j = 0; j < points.size(); j++) {
122 // cerr << j << ":: "<<points[j]<< endl;
123 // }
124 // vector<string> trans = REMatches(transregex,strokes[i],"TRANS");
125 // for (uint j = 0; j < trans.size(); j++) {
126 // cerr << j << "trans: "<<trans[j]<< endl;
127 // vector<string> matrix = REMatches(matrixregex,trans[j],"MATR");
128 // for (uint k = 0; k < matrix.size(); k++)
129 // cerr << k << " matr: "<<matrix[k]<< endl;
130 // }
131 // }
132 // vector<string> polys = REMatches(polyregex, lines, "POLY");
133 // for (uint i = 0; i < polys.size(); i++) {
134 // vector<string> points = REMatches(pointregex, polys[i], "POINT");
135 // Poly poly;
136 // //cout << i << ": ";
137 // //cout << polys[i] << endl;
138 // for (uint j = 0; j < points.size(); j++) {
139 // //cout << j << " - " << points[j] << endl ;
140 // istringstream is(points[j]);
141 // string type;
142 // is >> type;
143 // //cerr << type<< endl;
144 // if (type=="M" || type == "L") {
145 // double x,y;
146 // is >> x;
147 // is >> y;
148 // //cout << x << "," << y << endl;
149 // poly.addVertex(x,y);
150 // if (x<Min.x()) Min.x() = x;
151 // if (y<Min.y()) Min.y() = y;
152 // if (x>Max.x()) Max.x() = x;
153 // if (y>Max.y()) Max.y() = y;
154 // }
155 // }
156 // if (poly.size()>0) {
157 // poly.setZ(0);
158 // //cerr << poly.info() << endl;
159 // polygons.push_back(poly);
160 // }
161 // cout << endl;
162 // }
163 // }
164 // else cerr << _("Error: Unable to open SVG file - ") << filename << endl;
165 // Center = (Min+Max)/2;
166 // }
167
getPolygonsAtZ(const Matrix4d & T,double z,vector<Poly> & polys,double & max_grad,vector<Poly> & supportpolys,double max_supportangle,double thickness) const168 bool FlatShape::getPolygonsAtZ(const Matrix4d &T, double z,
169 vector<Poly> &polys, double &max_grad,
170 vector<Poly> &supportpolys,
171 double max_supportangle,
172 double thickness) const
173 {
174 max_grad = 0;
175 polys = polygons;
176 const Matrix4d trans = T * transform3D.transform;
177 for (uint i = 0; i < polys.size(); i++) {
178 polys[i].setZ(0);
179 polys[i].transform(trans);
180 }
181 return true;
182 }
183
clear()184 void FlatShape::clear()
185 {
186 polygons.clear();
187 }
188
draw_geometry(uint max_polygons)189 void FlatShape::draw_geometry(uint max_polygons) {
190 const Matrix4d invT = transform3D.getInverse();
191 const Vector3d minT = invT*Min;
192 const Vector3d maxT = invT*Max;
193 const Vector2d min2d(minT.x(), minT.y());
194 const Vector2d max2d(maxT.x(), maxT.y());
195 glDrawPolySurfaceRastered(polygons, min2d, max2d, 0, 0.1);
196 uint step = 1;
197 if (max_polygons > 0) step = polygons.size()/max_polygons;
198 for (uint i = 0; i < polygons.size(); i+=step) {
199 polygons[i].draw(GL_LINE_LOOP,false);
200 // Poly p;
201 // p.vertices = simplified(polygons[i].vertices, 0.2);
202 // cleandist(p.vertices, 0.2);
203 // p.draw_as_surface();
204
205 //polygons[i].draw_as_surface();
206 }
207 }
208
CalcBBox()209 void FlatShape::CalcBBox()
210 {
211 Min.set(INFTY,INFTY,0);
212 Max.set(-INFTY,-INFTY,0);
213 for(size_t i = 0; i < polygons.size(); i++)
214 for(size_t j = 0; j < polygons[i].size(); j++){
215 if ( polygons[i][j].x() < Min.x() ) Min.x() = polygons[i][j].x();
216 if ( polygons[i][j].y() < Min.y() ) Min.y() = polygons[i][j].y();
217 if ( polygons[i][j].x() > Max.x() ) Max.x() = polygons[i][j].x();
218 if ( polygons[i][j].y() > Max.y() ) Max.y() = polygons[i][j].y();
219 }
220 Min = transform3D.transform*Min;
221 Max = transform3D.transform*Max;
222 Center = (Max + Min )/2;
223 }
224
225
invertNormals()226 void FlatShape::invertNormals()
227 {
228 for (uint i = 0; i < polygons.size(); i++)
229 polygons[i].reverse();
230 }
231
mirror()232 void FlatShape::mirror()
233 {
234 for (uint i = 0; i < polygons.size(); i++)
235 polygons[i].mirrorX(Center);
236 }
237
238 // Rotate and adjust for the user - not a pure rotation by any means
Rotate(const Vector3d & axis,const double & angle)239 void FlatShape::Rotate(const Vector3d & axis, const double & angle)
240 {
241 CalcBBox();
242 if (axis.z()==0) return; // try to only 2D-rotate
243 Vector2d center(Center.x(),Center.y());
244 for (size_t i=0; i<polygons.size(); i++)
245 {
246 polygons[i].rotate(center, angle);
247 }
248 PlaceOnPlatform();
249 }
250
splitshapes(vector<Shape * > & shapes,ViewProgress * progress)251 void FlatShape::splitshapes(vector<Shape*> &shapes, ViewProgress *progress)
252 {
253 uint count = polygons.size();
254 if (progress) progress->start(_("Split Polygons"), count);
255 int progress_steps = max(1,(int)(count/100));
256
257 for (uint i = 0; i < count; i++) {
258 FlatShape *fs = new FlatShape();
259 fs->polygons.push_back(polygons[i]);
260 if (progress && i%progress_steps==0 && !progress->update(count)) break;
261 shapes.push_back(fs);
262 }
263 progress->stop("_(Done)");
264 }
265
266
info() const267 string FlatShape::info() const
268 {
269 ostringstream ostr;
270 ostr <<"FlatShape with "<<polygons.size() << " polygons "
271 << "min/max/center: "<<Min<<Max <<Center ;
272 return ostr.str();
273 }
274
275 ////////////////////////////// XML //////////////////////////////////////////
276
ToDouble(string s)277 inline double ToDouble(string s)
278 {
279 std::istringstream i(s);
280 double x;
281 if (!(i >> x))
282 return -1;
283 return x;
284 }
285
286 const Glib::RefPtr<Glib::Regex> polyregex =
287 Glib::Regex::create ("(?ims)<path.*?stroke\\:none.*?\\sd\\=\"(?<POLY>.*?(Z|\"/\\>))");
288 const Glib::RefPtr<Glib::Regex> strokeregex =
289 Glib::Regex::create ("(?ims)<path.*?(?<STROKE>fill\\:none.*?\\sd\\=\".*?\"/\\>)");
290 const Glib::RefPtr<Glib::Regex> strwidthregex =
291 Glib::Regex::create ("stroke\\-width\\:(?<STRWIDTH>[\\-\\.\\d]+)");
292 const Glib::RefPtr<Glib::Regex> pointregex =
293 Glib::Regex::create ("(?<POINT>[LM](\\s+[\\-\\.\\d]+){2})");
294 const Glib::RefPtr<Glib::Regex> transregex =
295 Glib::Regex::create ("transform=\"(?<TRANS>.*?)\"");
296 const Glib::RefPtr<Glib::Regex> matrixregex =
297 Glib::Regex::create ("matrix\\((?<MATR>([\\-\\.\\d]*?(\\,|\\))){6})");
298
299
svg_trans(const string & line)300 Matrix3d svg_trans(const string &line)
301 {
302 Matrix3d mat;
303 vector<string> val = REMatches(matrixregex, line, "MATR");
304 if (val.size()>0) {
305 vector<string> vals = Glib::Regex::split_simple("[\\,\\)]",val[0]);
306 if (vals.size()>5) {
307 mat.set_row(0,Vector3d(ToDouble(vals[0]),ToDouble(vals[2]),ToDouble(vals[4])));
308 mat.set_row(1,Vector3d(ToDouble(vals[1]),ToDouble(vals[3]),ToDouble(vals[5])));
309 mat.set_row(2,Vector3d(0,0,1));
310 }
311 }
312 return mat;
313 }
314
get_attr(const string & line,const string & attrname)315 string get_attr(const string &line, const string &attrname)
316 {
317 vector<string> parts = Glib::Regex::split_simple(";",line);
318 for (uint p = 0; p < parts.size(); p++) {
319 vector<string> lr = Glib::Regex::split_simple(":",parts[p]);
320 if (lr.size()==2){
321 if (lr[0] == attrname) return lr[1];
322 } else return "";
323 }
324 return "";
325 }
326
ToVertices(const string & line)327 vector<Vector2d> ToVertices(const string &line)
328 {
329 vector<string> points = REMatches(pointregex, line, "POINT");
330 vector<Vector2d> v;
331 for (uint j = 0; j < points.size(); j++) {
332 //cout << j << " - " << points[j] << endl ;
333 istringstream is(points[j]);
334 string type;
335 is >> type;
336 //cerr << type<< endl;
337 if (type=="M" || type == "L") {
338 double x,y;
339 if (is >> x && is >> y)
340 //cout << x << "," << y << endl;
341 v.push_back(Vector2d(x,y));
342 }
343 }
344 return v;
345 }
346
svg_addPolygon()347 int FlatShape::svg_addPolygon()
348 {
349
350 vector<Poly> polys;
351 if (svg_cur_style.find("stroke:none") != string::npos) { // polygon
352 Poly poly;
353 poly.vertices = ToVertices(svg_cur_path);
354 poly.setZ(0);
355 poly.reverse();
356 polys.push_back(poly);
357 }
358 else if (svg_cur_style.find("fill:none") != string::npos) { // stroke
359 // cerr << "stroke " << svg_cur_path << endl;
360 // cerr << "\t" << svg_cur_style << endl;
361 string wstr = get_attr(svg_cur_style,"stroke-width");
362 double width = ToDouble(wstr);
363 // cerr << "\t width " << wstr << " = " << width << endl;
364 vector<Vector2d> vertices = ToVertices(svg_cur_path);
365 polys = thick_lines(vertices, width);
366 // cerr <<"thick "<< polys.size()<<" of " << vertices.size() << endl;
367 }
368 else if (svg_cur_style!="") {
369 cerr << "unknown " << svg_cur_path << " in " << svg_cur_name << endl;
370 cerr << "\t style: " << svg_cur_style << endl;
371 }
372
373 if (polys.size()>0) {
374 if (svg_cur_trans!="") {
375 // cerr << svg_cur_trans << endl;
376 Matrix3d mat = svg_trans(svg_cur_trans);
377 // cerr << mat << endl;
378 for (uint i=0; i < polys.size(); i++) {
379 polys[i].setZ(0);
380 polys[i].transform(mat);
381 }
382 }
383 polygons.insert(polygons.begin(),polys.begin(),polys.end());
384 }
385 return polys.size();
386 }
387
388
xml_handle_node(const xmlpp::Node * node)389 void FlatShape::xml_handle_node(const xmlpp::Node* node)
390 {
391 //std::cout << std::endl; //Separate nodes by an empty line.
392
393 const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
394 const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(node);
395 const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(node);
396
397 // if(nodeText && nodeText->is_white_space()) //Let's ignore the indenting - you don't always want to do this.
398 // return;
399
400 const Glib::ustring nodename = node->get_name();
401
402 if(!nodeText && !nodeComment && !nodename.empty()) //Let's not say "name: text".
403 {
404 const Glib::ustring namespace_prefix = node->get_namespace_prefix();
405 if (svg_cur_name != "") svg_addPolygon();
406 svg_cur_name = nodename;
407 svg_cur_path = "";
408 svg_cur_trans = "";
409 svg_cur_style = "";
410 // if(namespace_prefix.empty())
411 // std::cout << "Node name = " << nodename << std::endl;
412 // else
413 // std::cout << "Node name = " << namespace_prefix << ":" << nodename << std::endl;
414 }
415 // else if(nodeText) //Let's say when it's text. - e.g. let's say what that white space is.
416 // {
417 // std::cout << "Text Node " << nodename << nodeText->get_content()<< std::endl;
418 // }
419
420 //Treat the various node types differently:
421 if(nodeText)
422 {
423 ; //std::cout << "text = \"" << nodeText->get_content() << "\"" << std::endl;
424 }
425 else if(nodeComment)
426 {
427 ; //std::cout << "comment = " << nodeComment->get_content() << std::endl;
428 }
429 else if(nodeContent)
430 {
431 ; //std::cout << "content = " << nodeContent->get_content() << std::endl;
432 } else
433 if (const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node)) {
434 //A normal Element node:
435
436 //line() works only for ElementNodes.
437 // std::cout << " line = " << node->get_line() << std::endl;
438
439 //attributes:
440 const xmlpp::Element::AttributeList& attributes = nodeElement->get_attributes();
441
442 for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin();
443 iter != attributes.end(); ++iter) {
444 const xmlpp::Attribute* attribute = *iter;
445
446 const Glib::ustring namespace_prefix = attribute->get_namespace_prefix();
447 string attr = attribute->get_name();
448 if (attr=="d")
449 svg_cur_path = attribute->get_value();
450 else if (attr == "style")
451 svg_cur_style = attribute->get_value();
452 else if (attr == "transform")
453 svg_cur_trans = attribute->get_value();
454 else if (svg_cur_name == "svg" ){
455 if (attr == "width" || attr == "height") {
456 string val = attribute->get_value();
457 if (val.find("pt") != string::npos)
458 svg_prescale = 0.3527;
459 }
460 }
461 else if (attr=="id") {
462 }
463 else
464 cerr << "unknown Attribute in " << svg_cur_name << " : " << attr << " = " <<attribute->get_value() << endl;
465 // if(namespace_prefix.empty())
466 // std::cout << " Attribute " << attribute->get_name() << " = " << << std::endl;
467 // else
468 // std::cout << " Attribute " << namespace_prefix << ":" << attribute->get_name() << " = " << attribute->get_value() << std::endl;
469 }
470
471 const xmlpp::Attribute* attribute = nodeElement->get_attribute("title");
472 if(attribute) {
473 std::cout << "title found: =" << attribute->get_value() << std::endl;
474 }
475 }
476
477 if(!nodeContent)
478 {
479 //Recurse through child nodes:
480 xmlpp::Node::NodeList list = node->get_children();
481 for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
482 {
483 xml_handle_node(*iter); //recursive
484 }
485 // get last bit (?)
486 if (svg_cur_name!="") svg_addPolygon();
487 }
488 }
489
490
loadSVG(string filename)491 int FlatShape::loadSVG(string filename)
492 { // Set the global C++ locale to the user-configured locale,
493 // so we can use std::cout with UTF-8, via Glib::ustring, without exceptions.
494
495 #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
496 try
497 {
498 #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
499 xmlpp::DomParser parser;
500 //parser.set_validate();
501 parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically.
502 parser.parse_file(filename);
503 if(parser)
504 {
505 polygons.clear();
506 svg_cur_name = "";
507 svg_cur_path = "";
508 svg_cur_trans = "";
509 svg_cur_style = "";
510 svg_prescale = 1.;
511 //Walk the tree:
512 const xmlpp::Node* pNode = parser.get_document()->get_root_node(); //deleted by DomParser.
513 xml_handle_node(pNode);
514 }
515
516 if (svg_prescale!=1)
517 for (uint i= 0; i<polygons.size(); i++)
518 for (uint j= 0; j<polygons[i].size(); j++)
519 polygons[i].vertices[j] *= svg_prescale;
520 Clipping clipp;
521 clipp.addPolys(polygons, subject);
522 polygons = clipp.unite(CL::pftNonZero,CL::pftNegative);
523 CalcBBox();
524 Vector2d center2(Center.x(),Center.y());
525 for (uint i= 0; i<polygons.size(); i++) {
526 polygons[i].mirrorX(Center);
527 polygons[i].rotate(center2, M_PI);
528 }
529 CalcBBox();
530
531 #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
532 }
533 catch(const std::exception& ex)
534 {
535 std::cerr << "Exception caught: " << ex.what() << std::endl;
536 }
537 #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
538
539 return 0;
540 }
541
542
543