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 &regex,
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