1 /*
2     This file is a part of the RepSnapper project.
3     Copyright (C) 2011 Michael Meeks
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser 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 #define  MODEL_IMPLEMENTATION
21 #include <vector>
22 #include <string>
23 #include <cerrno>
24 #include <functional>
25 #include <numeric>
26 
27 #include "stdafx.h"
28 #include "model.h"
29 #include "objtree.h"
30 #include "settings.h"
31 #include "layer.h"
32 #include "infill.h"
33 #include "ui/progress.h"
34 #include "shape.h"
35 #include "flatshape.h"
36 #include "render.h"
37 
Model()38 Model::Model() :
39   m_previewLayer(NULL),
40   //m_previewGCodeLayer(NULL),
41   currentprintingline(0),
42   settings(),
43   Min(), Max(),
44   m_inhibit_modelchange(false),
45   errlog (Gtk::TextBuffer::create()),
46   echolog (Gtk::TextBuffer::create()),
47   is_calculating(false),
48   is_printing(false)
49 {
50   // Variable defaults
51   Center.set(100.,100.,0.);
52   preview_shapes.clear();
53 }
54 
~Model()55 Model::~Model()
56 {
57   ClearLayers();
58   ClearGCode();
59   delete m_previewLayer;
60   preview_shapes.clear();
61 }
62 
alert(const char * message)63 void Model::alert (const char *message)
64 {
65   signal_alert.emit (Gtk::MESSAGE_INFO, message, NULL);
66 }
67 
error(const char * message,const char * secondary)68 void Model::error (const char *message, const char *secondary)
69 {
70   signal_alert.emit (Gtk::MESSAGE_ERROR, message, secondary);
71 }
72 
SaveConfig(Glib::RefPtr<Gio::File> file)73 void Model::SaveConfig(Glib::RefPtr<Gio::File> file)
74 {
75   settings.save_settings(file);
76 }
77 
LoadConfig(Glib::RefPtr<Gio::File> file)78 void Model::LoadConfig(Glib::RefPtr<Gio::File> file)
79 {
80   settings.load_settings(file);
81   ModelChanged();
82 }
83 
SimpleAdvancedToggle()84 void Model::SimpleAdvancedToggle()
85 {
86   cout << _("not yet implemented\n");
87 }
88 
SetViewProgress(ViewProgress * progress)89 void Model::SetViewProgress (ViewProgress *progress)
90 {
91   m_progress = progress;
92 }
93 
ClearGCode()94 void Model::ClearGCode()
95 {
96   m_previewGCode.clear();
97   m_previewGCode_z = -100000;
98   gcode.clear();
99 }
100 
ClearLayers()101 void Model::ClearLayers()
102 {
103   for(vector<Layer *>::iterator i=layers.begin(); i != layers.end(); i++) {
104     if ((*i)) (*i)->Clear();
105     delete *i;
106   }
107   layers.clear();
108   Infill::clearPatterns();
109   ClearPreview();
110 }
111 
ClearPreview()112 void Model::ClearPreview()
113 {
114   if (m_previewLayer) delete m_previewLayer;
115   m_previewLayer = NULL;
116   m_previewGCode.clear();
117   m_previewGCode_z = -100000;
118 }
119 
GetGCodeBuffer()120 Glib::RefPtr<Gtk::TextBuffer> Model::GetGCodeBuffer()
121 {
122   return gcode.buffer;
123 }
124 
GlDrawGCode(int layerno)125 void Model::GlDrawGCode(int layerno)
126 {
127   if (settings.get_boolean("Display","DisplayGCode"))  {
128     gcode.draw (settings, layerno, false);
129   }
130   // assume that the real printing line is the one at the start of the buffer
131   if (currentprintingline > 0) {
132     int currentlayer = gcode.getLayerNo(currentprintingline);
133     if (currentlayer>=0) {
134       int start = gcode.getLayerStart(currentlayer);
135       int end   = gcode.getLayerEnd(currentlayer);
136       //gcode.draw (settings, currentlayer, true, 1);
137       bool displaygcodeborders = settings.get_boolean("Display","DisplayGCodeBorders");
138       gcode.drawCommands(settings, start, currentprintingline, true, 4, false,
139 			 displaygcodeborders);
140       gcode.drawCommands(settings, currentprintingline,  end,  true, 1, false,
141 			 displaygcodeborders);
142     }
143     // gcode.drawCommands(settings, currentprintingline-currentbufferedlines,
144     // 		       currentprintingline, false, 3, true,
145     // 		       settings.Display.DisplayGCodeBorders);
146   }
147 }
148 
GlDrawGCode(double layerz)149 void Model::GlDrawGCode(double layerz)
150 {
151   if (!settings.get_boolean("Display","DisplayGCode")) return;
152   int layer = gcode.getLayerNo(layerz);
153   if (layer>=0)
154     GlDrawGCode(layer);
155 }
156 
157 
init()158 void Model::init() {}
159 
WriteGCode(Glib::RefPtr<Gio::File> file)160 void Model::WriteGCode(Glib::RefPtr<Gio::File> file)
161 {
162   Glib::ustring contents = gcode.get_text();
163   Glib::file_set_contents (file->get_path(), contents);
164   settings.GCodePath = file->get_parent()->get_path();
165 }
166 
ReadSVG(Glib::RefPtr<Gio::File> file)167 void Model::ReadSVG(Glib::RefPtr<Gio::File> file)
168 {
169   if (is_calculating) return;
170   if (is_printing) return;
171   bool autoplace = settings.get_boolean("Misc","ShapeAutoplace");
172   string path = file->get_path();
173   FlatShape * svgshape = new FlatShape(path);
174   cerr << svgshape->info() << endl;
175   AddShape(NULL, svgshape, path, autoplace);
176   ClearLayers();
177 }
178 
179 
ReadShapes(Glib::RefPtr<Gio::File> file,uint max_triangles)180 vector<Shape*> Model::ReadShapes(Glib::RefPtr<Gio::File> file,
181 				 uint max_triangles)
182 {
183   vector<Shape*> shapes;
184   if (!file) return shapes;
185   File sfile(file);
186   vector< vector<Triangle> > triangles;
187   vector<ustring> shapenames;
188   sfile.loadTriangles(triangles, shapenames, max_triangles);
189   for (uint i = 0; i < triangles.size(); i++) {
190     if (triangles[i].size() > 0) {
191       Shape *shape = new Shape();
192       shape->setTriangles(triangles[i]);
193       shape->filename = shapenames[i];
194       shape->FitToVolume(settings.getPrintVolume() - 2.*settings.getPrintMargin());
195       shapes.push_back(shape);
196     }
197   }
198   return shapes;
199 }
200 
201 
ReadStl(Glib::RefPtr<Gio::File> file)202 void Model::ReadStl(Glib::RefPtr<Gio::File> file)
203 {
204   bool autoplace = settings.get_boolean("Misc","ShapeAutoplace");
205   vector<Shape*> shapes = ReadShapes(file, 0);
206   // do not autoplace in multishape files
207   if (shapes.size() > 1)  autoplace = false;
208   for (uint i = 0; i < shapes.size(); i++){
209     AddShape(NULL, shapes[i], shapes[i]->filename, autoplace);
210   }
211   shapes.clear();
212   ModelChanged();
213 }
214 
SaveStl(Glib::RefPtr<Gio::File> file)215 void Model::SaveStl(Glib::RefPtr<Gio::File> file)
216 {
217   vector<Shape*> shapes;
218   vector<Matrix4d> transforms;
219   objtree.get_all_shapes(shapes,transforms);
220 
221   if(shapes.size() == 1) {
222     shapes[0]->saveBinarySTL(file->get_path());
223   }
224   else {
225     if (settings.get_boolean("Misc","SaveSingleShapeSTL")) {
226       Shape single = GetCombinedShape();
227       single.saveBinarySTL(file->get_path());
228     } else {
229       set_locales("C");
230       stringstream sstr;
231       for(uint s=0; s < shapes.size(); s++) {
232 	sstr << shapes[s]->getSTLsolid() << endl;
233       }
234       Glib::file_set_contents (file->get_path(), sstr.str());
235       reset_locales();
236     }
237   }
238   settings.STLPath = file->get_parent()->get_path();
239 }
240 
241 // everything in one shape
GetCombinedShape() const242 Shape Model::GetCombinedShape() const
243 {
244   Shape shape;
245   for (uint o = 0; o<objtree.Objects.size(); o++) {
246     for (uint s = 0; s<objtree.Objects[o]->shapes.size(); s++) {
247       vector<Triangle> tr =
248 	objtree.Objects[o]->shapes[s]->getTriangles(objtree.Objects[o]->transform3D.transform);
249       shape.addTriangles(tr);
250     }
251   }
252   return shape;
253 }
254 
SaveAMF(Glib::RefPtr<Gio::File> file)255 void Model::SaveAMF(Glib::RefPtr<Gio::File> file)
256 {
257   vector<Shape*> shapes;
258   vector<Matrix4d> transforms;
259   objtree.get_all_shapes(shapes,transforms);
260   vector< vector<Triangle> > triangles;
261   vector<ustring> names;
262   for(uint s = 0; s < shapes.size(); s++) {
263     triangles.push_back(shapes[s]->getTriangles(transforms[s]));
264     names.push_back(shapes[s]->filename);
265   }
266   File::save_AMF(file->get_path(), triangles, names);
267 }
268 
Read(Glib::RefPtr<Gio::File> file)269 void Model::Read(Glib::RefPtr<Gio::File> file)
270 {
271   std::string basename = file->get_basename();
272   size_t pos = basename.rfind('.');
273   cerr << "reading " << basename<< endl;
274   string directory_path = file->get_parent()->get_path();
275   if (pos != std::string::npos) {
276     std::string extn = basename.substr(pos);
277     if (extn == ".conf")
278       {
279 	LoadConfig (file);
280 	settings.SettingsPath = directory_path;
281 	return;
282       }
283     else if (extn == ".gcode")
284       {
285 	ReadGCode (file);
286 	settings.GCodePath = directory_path;
287 	return;
288       }
289     else if (extn == ".svg")
290       {
291 	ReadSVG (file);
292 	settings.STLPath = directory_path;
293 	return;
294       }
295     else if (extn == ".rfo")
296       {
297 	//      ReadRFO (file);
298 	settings.STLPath = directory_path;
299 	return;
300       }
301   }
302   ReadStl (file);
303   settings.STLPath = directory_path;
304 }
305 
ReadGCode(Glib::RefPtr<Gio::File> file)306 void Model::ReadGCode(Glib::RefPtr<Gio::File> file)
307 {
308   if (is_calculating) return;
309   if (is_printing) return;
310   is_calculating=true;
311   settings.set_boolean("Display","DisplayGCode",true);
312   m_progress->start (_("Reading GCode"), 100.0);
313   gcode.Read (this, settings.get_extruder_letters(), m_progress, file->get_path());
314   m_progress->stop (_("Done"));
315   is_calculating=false;
316   Max = gcode.Max;
317   Min = gcode.Min;
318   Center = (Max + Min) / 2.0;
319   m_signal_zoom.emit();
320 }
321 
322 
translateGCode(Vector3d trans)323 void Model::translateGCode(Vector3d trans)
324 {
325   if (is_calculating) return;
326   if (is_printing) return;
327   is_calculating=true;
328   gcode.translate(trans);
329 
330   string GcodeTxt;
331   gcode.MakeText (GcodeTxt, settings, m_progress);
332   Max = gcode.Max;
333   Min = gcode.Min;
334   Center = (Max + Min) / 2.0;
335   is_calculating=false;
336 }
337 
338 
339 
ModelChanged()340 void Model::ModelChanged()
341 {
342   if (m_inhibit_modelchange) return;
343   if (objtree.empty()) return;
344   //printer.update_temp_poll_interval(); // necessary?
345   if (!is_printing) {
346     CalcBoundingBoxAndCenter();
347     Infill::clearPatterns();
348     if ( layers.size()>0 || m_previewGCode.size()>0 || m_previewLayer ) {
349       ClearGCode();
350       ClearLayers();
351     }
352     setCurrentPrintingLine(0);
353     m_model_changed.emit();
354   }
355 }
356 
ClosestToOrigin(Vector3d a,Vector3d b)357 static bool ClosestToOrigin (Vector3d a, Vector3d b)
358 {
359   return (a.squared_length()) < (b.squared_length());
360 }
361 
362 // rearrange unselected shapes in random sequence
AutoArrange(vector<Gtk::TreeModel::Path> & path)363 bool Model::AutoArrange(vector<Gtk::TreeModel::Path> &path)
364 {
365   // all shapes
366   vector<Shape*>   allshapes;
367   vector<Matrix4d> transforms;
368   objtree.get_all_shapes(allshapes, transforms);
369 
370   // selected shapes
371   vector<Shape*>   selshapes;
372   vector<Matrix4d> seltransforms;
373   objtree.get_selected_shapes(path, selshapes, seltransforms);
374 
375   // get unselected shapes
376   vector<Shape*>   unselshapes;
377   vector<Matrix4d> unseltransforms;
378 
379   for(uint s=0; s < allshapes.size(); s++) {
380     bool issel = false;
381     for(uint ss=0; ss < selshapes.size(); ss++)
382       if (selshapes[ss] == allshapes[s]) {
383 	issel = true; break;
384       }
385     if (!issel) {
386       unselshapes.    push_back(allshapes[s]);
387       unseltransforms.push_back(transforms[s]);
388     }
389   }
390 
391   // find place for unselected shapes
392   int num = unselshapes.size();
393   vector<int> rand_seq(num,1); // 1,1,1...
394   partial_sum(rand_seq.begin(), rand_seq.end(), rand_seq.begin()); // 1,2,3,...,N
395 
396   Glib::TimeVal timeval;
397   timeval.assign_current_time();
398   srandom((unsigned long)(timeval.as_double()));
399   random_shuffle(rand_seq.begin(), rand_seq.end()); // shuffle
400 
401   for(int s=0; s < num; s++) {
402     int index = rand_seq[s]-1;
403     // use selshapes as vector to fill up
404     Vector3d trans = FindEmptyLocation(selshapes, seltransforms, unselshapes[index]);
405     selshapes.push_back(unselshapes[index]);
406     seltransforms.push_back(unseltransforms[index]); // basic transform, not shape
407     selshapes.back()->transform3D.move(trans);
408     CalcBoundingBoxAndCenter();
409   }
410   ModelChanged();
411   return true;
412 }
413 
FindEmptyLocation(const vector<Shape * > & shapes,const vector<Matrix4d> & transforms,const Shape * shape)414 Vector3d Model::FindEmptyLocation(const vector<Shape*> &shapes,
415 				  const vector<Matrix4d> &transforms,
416 				  const Shape *shape)
417 {
418   // Get all object positions
419   std::vector<Vector3d> maxpos;
420   std::vector<Vector3d> minpos;
421   for(uint s=0; s<shapes.size(); s++) {
422     Vector3d p;
423     Matrix4d strans = transforms[s];
424     Vector3d min = strans * shapes[s]->Min;
425     Vector3d max = strans * shapes[s]->Max;
426     minpos.push_back(Vector3d(min.x(), min.y(), 0));
427     maxpos.push_back(Vector3d(max.x(), max.y(), 0));
428   }
429 
430   double d = 5.0; // 5mm spacing between objects
431   Vector3d StlDelta = (shape->Max - shape->Min);
432   vector<Vector3d> candidates;
433 
434   candidates.push_back(Vector3d(0.0, 0.0, 0.0));
435 
436   for (uint j=0; j<maxpos.size(); j++)
437   {
438     candidates.push_back(Vector3d(maxpos[j].x() + d, minpos[j].y(), 0));
439     candidates.push_back(Vector3d(minpos[j].x(), maxpos[j].y() + d, 0));
440     candidates.push_back(maxpos[j] + Vector3d(d,d,0));
441   }
442 
443   // Prefer positions closest to origin
444   sort(candidates.begin(), candidates.end(), ClosestToOrigin);
445 
446 
447   Vector3d result;
448   // Check all candidates for collisions with existing objects
449   for (uint c=0; c<candidates.size(); c++)
450   {
451     bool ok = true;
452 
453     for (uint k=0; k<maxpos.size(); k++)
454     {
455       if (
456           // check x
457           ( ( ( minpos[k].x()     <= candidates[c].x() &&
458 		candidates[c].x() <= maxpos[k].x() ) ||
459 	      ( candidates[c].x() <= minpos[k].x() &&
460 		maxpos[k].x() <= candidates[c].x()+StlDelta.x()+d ) ) ||
461 	    ( ( minpos[k].x() <= candidates[c].x()+StlDelta.x()+d &&
462 		candidates[c].x()+StlDelta.x()+d <= maxpos[k].x() ) ) )
463           &&
464           // check y
465           ( ( ( minpos[k].y() <= candidates[c].y() &&
466 		candidates[c].y() <= maxpos[k].y() ) ||
467 	      ( candidates[c].y() <= minpos[k].y() &&
468 		maxpos[k].y() <= candidates[c].y()+StlDelta.y()+d ) ) ||
469 	    ( ( minpos[k].y() <= candidates[c].y()+StlDelta.y()+d &&
470 		candidates[c].y()+StlDelta.y()+d <= maxpos[k].y() ) ) )
471 	  )
472 	{
473 	  ok = false;
474 	  break;
475 	}
476 
477       // volume boundary
478       if (candidates[c].x()+StlDelta.x() >
479 	  (settings.getPrintVolume().x() - 2*settings.getPrintMargin().x())
480 	  || candidates[c].y()+StlDelta.y() >
481 	  (settings.getPrintVolume().y() - 2*settings.getPrintMargin().y()))
482 	{
483 	  ok = false;
484 	  break;
485 	}
486     }
487     if (ok) {
488       result.x() = candidates[c].x();
489       result.y() = candidates[c].y();
490       result.z() = candidates[c].z();
491       // keep z
492       result.x() -= shape->Min.x();
493       result.y() -= shape->Min.y();
494       return result;
495     }
496   }
497 
498   // no empty spots
499   return Vector3d(100,100,0);
500 }
501 
FindEmptyLocation(Vector3d & result,const Shape * shape)502 bool Model::FindEmptyLocation(Vector3d &result, const Shape *shape)
503 {
504   // Get all object positions
505   std::vector<Vector3d> maxpos;
506   std::vector<Vector3d> minpos;
507 
508   vector<Shape*>   allshapes;
509   vector<Matrix4d> transforms;
510   objtree.get_all_shapes(allshapes, transforms);
511   result = FindEmptyLocation(allshapes, transforms, shape);
512   return true;
513 }
514 
AddShape(TreeObject * parent,Shape * shape,string filename,bool autoplace)515 int Model::AddShape(TreeObject *parent, Shape *shape, string filename, bool autoplace)
516 {
517   //Shape *retshape;
518   bool found_location=false;
519 
520 
521   FlatShape* flatshape = dynamic_cast<FlatShape*>(shape);
522   if (flatshape != NULL)
523     shape = flatshape;
524 
525   if (!parent) {
526     if (objtree.Objects.size() <= 0)
527       objtree.newObject();
528     parent = objtree.Objects.back();
529   }
530   g_assert (parent != NULL);
531 
532   // Decide where it's going
533   Vector3d trans = Vector3d(0,0,0);
534   if (autoplace) found_location = FindEmptyLocation(trans, shape);
535   // Add it to the set
536   size_t found = filename.find_last_of("/\\");
537   Gtk::TreePath path = objtree.addShape(parent, shape, filename.substr(found+1));
538   Shape *retshape = parent->shapes.back();
539 
540   // Move it, if we found a suitable place
541   if (found_location) {
542     retshape->transform3D.move(trans);
543   }
544 
545   //if (autoplace) retshape->PlaceOnPlatform();
546 
547   // Update the view to include the new object
548   ModelChanged();
549     // Tell everyone
550   m_signal_stl_added.emit (path);
551 
552   return 0;
553 }
554 
SplitShape(TreeObject * parent,Shape * shape,string filename)555 int Model::SplitShape(TreeObject *parent, Shape *shape, string filename)
556 {
557   vector<Shape*> splitshapes;
558   shape->splitshapes(splitshapes, m_progress);
559   if (splitshapes.size()<2) return splitshapes.size();
560   for (uint s = 0; s <  splitshapes.size(); s++) {
561     ostringstream sfn;
562     sfn << filename << "_" << (s+1) ;
563     AddShape(parent, splitshapes[s], sfn.str(), false);
564   }
565   return splitshapes.size();
566 }
567 
MergeShapes(TreeObject * parent,const vector<Shape * > shapes)568 int Model::MergeShapes(TreeObject *parent, const vector<Shape*> shapes)
569 {
570   Shape * shape = new Shape();
571   for (uint s = 0; s <  shapes.size(); s++) {
572     vector<Triangle> str = shapes[s]->getTriangles();
573     shape->addTriangles(str);
574   }
575   AddShape(parent, shape, "merged", true);
576   return 1;
577 }
578 
DivideShape(TreeObject * parent,Shape * shape,string filename)579 int Model::DivideShape(TreeObject *parent, Shape *shape, string filename)
580 {
581   Shape *upper = new Shape();
582   Shape *lower = new Shape();
583   Matrix4d T = Matrix4d::IDENTITY;;//FIXME! objtree.GetSTLTransformationMatrix(parent);
584   int num = shape->divideAtZ(0, upper, lower, T);
585   if (num<2) return num;
586   else if (num==2) {
587     AddShape(parent, upper, filename+_("_upper") ,false);
588     AddShape(parent, lower, filename+_("_lower") ,false);
589   }
590   return num;
591 }
592 
newObject()593 void Model::newObject()
594 {
595   objtree.newObject();
596 }
597 
598 /* Scales the object on changes of the scale slider */
ScaleObject(Shape * shape,TreeObject * object,double scale)599 void Model::ScaleObject(Shape *shape, TreeObject *object, double scale)
600 {
601   if (shape)
602     shape->Scale(scale);
603   else if(object)
604     //    for (uint s = 0;s<object->shapes.size(); s++) {
605       //double fact = object->shapes[s].getScaleFactor();
606       object->transform3D.scale(scale);
607   //}
608   else return;
609   ModelChanged();
610 }
ScaleObjectX(Shape * shape,TreeObject * object,double scale)611 void Model::ScaleObjectX(Shape *shape, TreeObject *object, double scale)
612 {
613   if (shape)
614     shape->ScaleX(scale);
615   else if(object)
616     for (uint s = 0;s<object->shapes.size(); s++) {
617       //double fact = object->shapes[s].getScaleFactor();
618       object->shapes[s]->ScaleX(scale);
619     }
620   else return;
621   ModelChanged();
622 }
ScaleObjectY(Shape * shape,TreeObject * object,double scale)623 void Model::ScaleObjectY(Shape *shape, TreeObject *object, double scale)
624 {
625   if (shape)
626     shape->ScaleY(scale);
627   else if(object)
628     for (uint s = 0;s<object->shapes.size(); s++) {
629       //double fact = object->shapes[s].getScaleFactor();
630       object->shapes[s]->ScaleY(scale);
631     }
632   else return;
633   ModelChanged();
634 }
ScaleObjectZ(Shape * shape,TreeObject * object,double scale)635 void Model::ScaleObjectZ(Shape *shape, TreeObject *object, double scale)
636 {
637   if (shape)
638     shape->ScaleZ(scale);
639   else if(object)
640     for (uint s = 0;s<object->shapes.size(); s++) {
641       //      double fact = object->shapes[s].getScaleFactorZ();
642       object->shapes[s]->ScaleZ(scale);
643     }
644   else return;
645   ModelChanged();
646 }
647 
RotateObject(Shape * shape,TreeObject * object,Vector4d rotate)648 void Model::RotateObject(Shape* shape, TreeObject* object, Vector4d rotate)
649 {
650   if (!shape)
651     return;
652   Vector3d rot(rotate.x(), rotate.y(), rotate.z());
653   shape->Rotate(rot, rotate.w());
654   ModelChanged();
655 }
656 
TwistObject(Shape * shape,TreeObject * object,double angle)657 void Model::TwistObject(Shape *shape, TreeObject *object, double angle)
658 {
659   if (!shape)
660     return;
661   shape->Twist(angle);
662   ModelChanged();
663 }
664 
OptimizeRotation(Shape * shape,TreeObject * object)665 void Model::OptimizeRotation(Shape *shape, TreeObject *object)
666 {
667   if (!shape)
668     return; // FIXME: rotate entire Objects ...
669   shape->OptimizeRotation();
670   ModelChanged();
671 }
672 
InvertNormals(Shape * shape,TreeObject * object)673 void Model::InvertNormals(Shape *shape, TreeObject *object)
674 {
675   if (shape)
676     shape->invertNormals();
677   else // if (object) object->invertNormals();
678     return;
679   ModelChanged();
680 }
Mirror(Shape * shape,TreeObject * object)681 void Model::Mirror(Shape *shape, TreeObject *object)
682 {
683   if (shape)
684     shape->mirror();
685   else // if (object) object->mirror();
686     return;
687   ModelChanged();
688 }
689 
PlaceOnPlatform(Shape * shape,TreeObject * object)690 void Model::PlaceOnPlatform(Shape *shape, TreeObject *object)
691 {
692   if (shape)
693     shape->PlaceOnPlatform();
694   else if(object) {
695     Transform3D * transf = &object->transform3D;
696     transf->move(Vector3f(0, 0, -transf->getTranslation().z()));
697     for (uint s = 0;s<object->shapes.size(); s++) {
698       object->shapes[s]->PlaceOnPlatform();
699     }
700   }
701   else return;
702   ModelChanged();
703 }
704 
DeleteObjTree(vector<Gtk::TreeModel::Path> & iter)705 void Model::DeleteObjTree(vector<Gtk::TreeModel::Path> &iter)
706 {
707   objtree.DeleteSelected (iter);
708   ClearGCode();
709   ClearLayers();
710   ModelChanged();
711 }
712 
713 
ClearLogs()714 void Model::ClearLogs()
715 {
716   errlog->set_text("");
717   echolog->set_text("");
718 }
719 
CalcBoundingBoxAndCenter(bool selected_only)720 void Model::CalcBoundingBoxAndCenter(bool selected_only)
721 {
722   Vector3d newMax = Vector3d(G_MINDOUBLE, G_MINDOUBLE, G_MINDOUBLE);
723   Vector3d newMin = Vector3d(G_MAXDOUBLE, G_MAXDOUBLE, G_MAXDOUBLE);
724 
725   vector<Shape*> shapes;
726   vector<Matrix4d> transforms;
727   if (selected_only)
728     objtree.get_selected_shapes(m_current_selectionpath, shapes, transforms);
729   else
730     objtree.get_all_shapes(shapes, transforms);
731 
732   for (uint s = 0 ; s < shapes.size(); s++) {
733     shapes[s]->CalcBBox();
734     Vector3d stlMin = transforms[s] * shapes[s]->Min;
735     Vector3d stlMax = transforms[s] * shapes[s]->Max;
736     for (uint k = 0; k < 3; k++) {
737       newMin[k] = MIN(stlMin[k], newMin[k]);
738       newMax[k] = MAX(stlMax[k], newMax[k]);
739     }
740   }
741 
742   // for (uint i = 0 ; i < objtree.Objects.size(); i++) {
743   //   Matrix4d M = objtree.getTransformationMatrix (i);
744   //   for (uint j = 0; j < objtree.Objects[i]->shapes.size(); j++) {
745   //     objtree.Objects[i]->shapes[j]->CalcBBox();
746   //     Vector3d stlMin = M * objtree.Objects[i]->shapes[j]->Min;
747   //     Vector3d stlMax = M * objtree.Objects[i]->shapes[j]->Max;
748   //     for (uint k = 0; k < 3; k++) {
749   // 	newMin[k] = MIN(stlMin[k], newMin[k]);
750   // 	newMax[k] = MAX(stlMax[k], newMax[k]);
751   //     }
752   //   }
753   // }
754 
755   if (newMin.x() > newMax.x()) {
756     // Show the whole platform if there's no objects
757     Min = Vector3d(0,0,0);
758     Vector3d pM = settings.getPrintMargin();
759     Max = settings.getPrintVolume() - pM - pM;
760     Max.z() = 0;
761   }
762   else {
763     Max = newMax;
764     Min = newMin;
765   }
766 
767   Center = (Max + Min) / 2.0;
768   m_signal_zoom.emit();
769 }
770 
GetViewCenter()771 Vector3d Model::GetViewCenter()
772 {
773   Vector3d printOffset = settings.getPrintMargin();
774   if(settings.get_boolean("Raft","Enable")){
775     const double rsize = settings.get_double("Raft","Size");
776     printOffset += Vector3d(rsize, rsize, 0);
777   }
778   return printOffset + Center;
779 }
780 
781 // called from View::Draw
draw(vector<Gtk::TreeModel::Path> & iter)782 int Model::draw (vector<Gtk::TreeModel::Path> &iter)
783 {
784   vector<Shape*> sel_shapes;
785   vector<Matrix4d> transforms;
786   objtree.get_selected_shapes(iter, sel_shapes, transforms);
787 
788   gint index = 1; // pick/select index. matches computation in update_model()
789 
790   Vector3d printOffset = settings.getPrintMargin();
791   if(settings.get_boolean("Raft","Enable")) {
792     const double rsize = settings.get_double("Raft","Size");
793     printOffset += Vector3d(rsize, rsize, 0);
794   }
795   Vector3d translation = objtree.transform3D.getTranslation();
796   Vector3d offset = printOffset + translation;
797 
798   // Add the print offset to the drawing location of the STL objects.
799   glTranslated(offset.x(),offset.y(),offset.z());
800 
801   glPushMatrix();
802   glMultMatrixd (&objtree.transform3D.transform.array[0]);
803 
804   // draw preview shapes and nothing else
805   if (settings.get_boolean("Display","PreviewLoad"))
806     if (preview_shapes.size() > 0) {
807       Vector3d v_center = GetViewCenter() - offset;
808       glTranslated( v_center.x(), v_center.y(), v_center.z());
809       for (uint i = 0; i < preview_shapes.size(); i++) {
810 	offset = preview_shapes[i]->t_Center();
811 	glTranslated(offset.x(), offset.y(), offset.z());
812 	// glPushMatrix();
813 	// glMultMatrixd (&preview_shapes[i]->transform3D.transform.array[0]);
814 	preview_shapes[i]->draw (settings, false, 20000);
815 	preview_shapes[i]->drawBBox ();
816 	// glPopMatrix();
817       }
818       glPopMatrix();
819       glPopMatrix();
820       return 0;
821     }
822   bool support = settings.get_boolean("Slicing","Support");
823   double supportangle = settings.get_double("Slicing","SupportAngle");
824   bool displaypolygons = settings.get_boolean("Display","DisplayPolygons");
825   bool displaybbox = settings.get_boolean("Display","DisplayBBox");
826   for (uint i = 0; i < objtree.Objects.size(); i++) {
827     TreeObject *object = objtree.Objects[i];
828     index++;
829 
830     glPushMatrix();
831     glMultMatrixd (&object->transform3D.transform.array[0]);
832     for (uint j = 0; j < object->shapes.size(); j++) {
833       Shape *shape = object->shapes[j];
834       glLoadName(index); // Load select/pick index
835       index++;
836       glPushMatrix();
837       glMultMatrixd (&shape->transform3D.transform.array[0]);
838 
839       bool is_selected = false;
840       for (uint s = 0; s < sel_shapes.size(); s++)
841 	if (sel_shapes[s] == shape)
842 	  is_selected = true;
843 
844       // this is slow for big shapes
845       if (is_selected) {
846 	if (!shape->slow_drawing && shape->dimensions()>2) {
847 	  // Enable stencil buffer when we draw the selected object.
848 	  glEnable(GL_STENCIL_TEST);
849 	  glStencilFunc(GL_ALWAYS, 1, 1);
850 	  glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
851 
852 	  shape->draw (settings);
853 
854 	  if (!displaypolygons) {
855 	    // If not drawing polygons, need to draw the geometry
856 	    // manually, but invisible, to set up the stencil buffer
857 	    glEnable(GL_CULL_FACE);
858 	    glEnable(GL_DEPTH_TEST);
859 	    glEnable(GL_BLEND);
860 	    // Set to not draw anything, and not update depth buffer
861 	    glDepthMask(GL_FALSE);
862 	    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
863 
864 	    shape->draw_geometry();
865 
866 	    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
867 	    glDepthMask(GL_TRUE);
868 	  }
869 
870 	  // draw highlight around selected object
871 	  glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
872 	  glLineWidth(3.0);
873 	  glEnable (GL_POLYGON_OFFSET_LINE);
874 
875 	  glDisable (GL_CULL_FACE);
876 	  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
877 	  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
878 	  glStencilFunc(GL_NOTEQUAL, 1, 1);
879 	  glEnable(GL_DEPTH_TEST);
880 
881 	  shape->draw_geometry();
882 
883 	  glEnable (GL_CULL_FACE);
884 	  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
885 	  glDisable(GL_STENCIL_TEST);
886 	  glDisable(GL_POLYGON_OFFSET_LINE);
887 	}
888 	else shape->draw (settings, true);
889       }
890       else {
891 	shape->draw (settings, false);
892       }
893       // draw support triangles
894       if (support) {
895 	glColor4f(0.8f,0.f,0.f,0.5f);
896 	vector<Triangle> suppTr =
897 	  shape->trianglesSteeperThan(supportangle*M_PI/180.);
898 	for (uint i=0; i < suppTr.size(); i++)
899 	  suppTr[i].draw(GL_TRIANGLES);
900       }
901       glPopMatrix();
902       if(displaybbox)
903 	shape->drawBBox();
904      }
905     glPopMatrix();
906   }
907   glPopMatrix();
908   glLoadName(0); // Clear selection name to avoid selecting last object with later rendering.
909 
910   // draw total bounding box
911   if(displaybbox)
912     {
913       const double minz = max(0., Min.z()); // above xy plane only
914       // Draw bbox
915       glDisable(GL_DEPTH_TEST);
916       glLineWidth(1);
917       glColor3f(1,0,0);
918       glBegin(GL_LINE_LOOP);
919       glVertex3f(Min.x(), Min.y(), minz);
920       glVertex3f(Min.x(), Max.y(), minz);
921       glVertex3f(Max.x(), Max.y(), minz);
922       glVertex3f(Max.x(), Min.y(), minz);
923       glEnd();
924       glBegin(GL_LINE_LOOP);
925       glVertex3f(Min.x(), Min.y(), Max.z());
926       glVertex3f(Min.x(), Max.y(), Max.z());
927       glVertex3f(Max.x(), Max.y(), Max.z());
928       glVertex3f(Max.x(), Min.y(), Max.z());
929       glEnd();
930       glBegin(GL_LINES);
931       glVertex3f(Min.x(), Min.y(), minz);
932       glVertex3f(Min.x(), Min.y(), Max.z());
933       glVertex3f(Min.x(), Max.y(), minz);
934       glVertex3f(Min.x(), Max.y(), Max.z());
935       glVertex3f(Max.x(), Max.y(), minz);
936       glVertex3f(Max.x(), Max.y(), Max.z());
937       glVertex3f(Max.x(), Min.y(), minz);
938       glVertex3f(Max.x(), Min.y(), Max.z());
939       glEnd();
940       glColor3f(1,0.6,0.6);
941       ostringstream val;
942       val.precision(1);
943       Vector3d pos;
944       val << fixed << (Max.x()-Min.x());
945       pos = Vector3d((Max.x()+Min.x())/2.,Min.y(),Max.z());
946       Render::draw_string(pos,val.str());
947       val.str("");
948       val << fixed << (Max.y()-Min.y());
949       pos = Vector3d(Min.x(),(Max.y()+Min.y())/2.,Max.z());
950       Render::draw_string(pos,val.str());
951       val.str("");
952       val << fixed << (Max.z()-minz);
953       pos = Vector3d(Min.x(),Min.y(),(Max.z()+minz)/2.);
954       Render::draw_string(pos,val.str());
955     }
956   int drawnlayer = -1;
957   if(settings.get_boolean("Display","DisplayLayer")) {
958     drawnlayer = drawLayers(settings.get_double("Display","LayerValue"),
959 			    offset, false);
960   }
961   if(settings.get_boolean("Display","DisplayGCode") && gcode.size() == 0) {
962     // preview gcode if not calculated yet
963     if ( m_previewGCode.size() != 0 ||
964 	 ( layers.size() == 0 && gcode.commands.size() == 0 ) ) {
965       Vector3d start(0,0,0);
966       const double thickness = settings.get_double("Slicing","LayerThickness");
967       const double gcodedrawstart = settings.get_double("Display","GCodeDrawStart");
968       const double z = gcodedrawstart + thickness/2;
969       const int LayerCount = (int)ceil(Max.z()/thickness)-1;
970       const uint LayerNo = (uint)ceil(gcodedrawstart*(LayerCount-1));
971       if (z != m_previewGCode_z) {
972 	//uint prevext = settings.selectedExtruder;
973 	Layer * previewGCodeLayer = calcSingleLayer(z, LayerNo, thickness, true, true);
974 	if (previewGCodeLayer) {
975 	  m_previewGCode.clear();
976 	  vector<Command> commands;
977 	  GCodeState state(m_previewGCode);
978 	  previewGCodeLayer->MakeGCode(start, state, 0, settings);
979 	  // state.AppendCommands(commands, settings.Slicing.RelativeEcode);
980 	  m_previewGCode_z = z;
981 	}
982 	//settings.SelectExtruder(prevext);
983       }
984       glDisable(GL_DEPTH_TEST);
985       m_previewGCode.drawCommands(settings, 1, m_previewGCode.commands.size(), true, 2,
986 				  settings.get_boolean("Display","DisplayGCodeArrows"),
987 				  settings.get_boolean("Display","DisplayGCodeBorders"));
988     }
989   }
990   return drawnlayer;
991 }
992 
993 // if single layer returns layerno of drawn layer
994 // else returns -1
drawLayers(double height,const Vector3d & offset,bool calconly)995 int Model::drawLayers(double height, const Vector3d &offset, bool calconly)
996 {
997   if (is_calculating) return -1; // infill calculation (saved patterns) would be disturbed
998 
999   glDisable(GL_DEPTH_TEST);
1000   int drawn = -1;
1001   int LayerNr;
1002 
1003  ;
1004 
1005   bool have_layers = (layers.size() > 0); // have sliced already
1006 
1007   bool fillAreas = settings.get_boolean("Display","DisplayFilledAreas");
1008 
1009   double minZ = 0;//max(0.0, Min.z());
1010   double z;
1011   double zStep = settings.get_double("Slicing","LayerThickness");
1012   double zSize = (Max.z() - minZ - zStep*0.5);
1013   int LayerCount = (int)ceil((zSize - zStep*0.5)/zStep)-1;
1014   double sel_Z = height; //*zSize;
1015   uint sel_Layer;
1016   if (have_layers)
1017     sel_Layer = (uint)floor(height*(layers.size())/zSize);
1018   else
1019     sel_Layer = (uint)ceil(LayerCount*sel_Z/zSize);
1020   LayerCount = sel_Layer+1;
1021   if(have_layers && settings.get_boolean("Display","DisplayAllLayers"))
1022     {
1023       LayerNr = 0;
1024       z=minZ;
1025       // don't fill areas if multiple layers
1026       settings.set_boolean("Display","DisplayFilledAreas",false);
1027     }
1028   else
1029     {
1030       LayerNr = sel_Layer;
1031       z= minZ + sel_Z;
1032     }
1033   if (have_layers) {
1034     LayerNr = CLAMP(LayerNr, 0, (int)layers.size() - 1);
1035     LayerCount = CLAMP(LayerCount, 0, (int)layers.size());
1036   }
1037   z = CLAMP(z, 0, Max.z());
1038   z += 0.5*zStep; // always cut in middle of layer
1039 
1040   //cerr << zStep << ";"<<Max.z()<<";"<<Min.z()<<";"<<zSize<<";"<<LayerNr<<";"<<LayerCount<<";"<<endl;
1041 
1042   Layer* layer=NULL;
1043   if (have_layers)
1044     glTranslatef(-offset.x(), -offset.y(), -offset.z());
1045 
1046   const float lthickness = settings.get_double("Slicing","LayerThickness");
1047   bool displayinfill = settings.get_boolean("Display","DisplayinFill");
1048   bool drawrulers = settings.get_boolean("Display","DrawRulers");
1049   while(LayerNr < LayerCount)
1050     {
1051       if (have_layers)
1052 	{
1053 	  layer = layers[LayerNr];
1054 	  z = layer->getZ();
1055 	  drawn = layer->LayerNo;
1056 	}
1057       else
1058 	{
1059 	  if (!m_previewLayer || m_previewLayer->getZ() != z) {
1060 	    m_previewLayer = calcSingleLayer(z, LayerNr, lthickness,
1061 					     displayinfill, false);
1062 	    layer = m_previewLayer;
1063 	    Layer * previous = NULL;
1064 	    if (LayerNr>0 && z >= lthickness)
1065 	      previous = calcSingleLayer(z-lthickness, LayerNr-1, lthickness,
1066 					 false, false);
1067 	    layer->setPrevious(previous);
1068 	  }
1069 	  layer = m_previewLayer;
1070 	}
1071       if (!calconly) {
1072 	layer->Draw(settings);
1073 
1074 	if (drawrulers)
1075 	  layer->DrawRulers(measuresPoint);
1076       }
1077 
1078       // if (!have_layers)
1079       // {
1080       //       // need to delete the temporary  layer
1081       //       delete layer;
1082       // }
1083       LayerNr++;
1084       z+=zStep;
1085     }// while
1086 
1087   settings.set_boolean("Display","DisplayFilledAreas", fillAreas); // set to value before
1088   return drawn;
1089 }
1090 
1091 
calcSingleLayer(double z,uint LayerNr,double thickness,bool calcinfill,bool for_gcode) const1092 Layer * Model::calcSingleLayer(double z, uint LayerNr, double thickness,
1093 			       bool calcinfill, bool for_gcode) const
1094 {
1095   if (is_calculating) return NULL; // infill calculation (saved patterns) would be disturbed
1096   if (!for_gcode) {
1097     if (m_previewLayer && m_previewLayer->getZ() == z
1098 	&& m_previewLayer->thickness == thickness) return m_previewLayer;
1099   }
1100   vector<Shape*> shapes;
1101   vector<Matrix4d> transforms;
1102 
1103   if (settings.get_boolean("Slicing","SelectedOnly"))
1104     objtree.get_selected_shapes(m_current_selectionpath, shapes, transforms);
1105   else
1106     objtree.get_all_shapes(shapes, transforms);
1107 
1108   double max_grad = 0;
1109   double supportangle = settings.get_double("Slicing","SupportAngle")*M_PI/180.;
1110   if (!settings.get_boolean("Slicing","Support")) supportangle = -1;
1111 
1112   Layer * layer = new Layer(NULL, LayerNr, thickness,
1113 			    settings.get_integer("Slicing","Skins"));
1114   layer->setZ(z);
1115   for(size_t f = 0; f < shapes.size(); f++) {
1116     layer->addShape(transforms[f], *shapes[f], z, max_grad, supportangle);
1117   }
1118 
1119   // vector<Poly> polys = layer->GetPolygons();
1120   // for (guint i=0; i<polys.size();i++){
1121   //   vector<Triangle> tri;
1122   //   polys[i].getTriangulation(tri);
1123   //   for (guint j=0; j<tri.size();j++){
1124   //     tri[j].draw(GL_LINE_LOOP);
1125   //   }
1126   // }
1127 
1128   layer->MakeShells(settings);
1129 
1130   if (settings.get_boolean("Slicing","Skirt")) {
1131     if (layer->getZ() - layer->thickness <= settings.get_double("Slicing","SkirtHeight"))
1132       layer->MakeSkirt(settings.get_double("Slicing","SkirtDistance"),
1133 		       settings.get_boolean("Slicing","SingleSkirt") &&
1134 		       !settings.get_boolean("Slicing","Support"));
1135   }
1136 
1137   if (calcinfill)
1138     layer->CalcInfill(settings);
1139 
1140 #define DEBUGPOLYS 0
1141 #if DEBUGPOLYS
1142   // write out polygons for gnuplot
1143   vector<Poly> polys = layer->GetPolygons();
1144   vector< vector<Poly> > offs = layer->GetShellPolygons();
1145   cout << "# polygons "<< endl;
1146   for (guint i=0; i<polys.size();i++){
1147     cout << polys[i].gnuplot_path() << endl;
1148   }
1149   for (guint s=0; s<offs.size();s++){
1150     cout << "# offset polygons " << s << endl;
1151     for (guint i=0; i<offs[s].size();i++){
1152       cout << offs[s][i].gnuplot_path() << endl;
1153     }
1154   }
1155 #endif
1156 
1157   return layer;
1158 }
1159 
1160 
get_preview_Z()1161 double Model::get_preview_Z()
1162 {
1163   if (m_previewLayer) return m_previewLayer->getZ();
1164   return 0;
1165 }
1166 
setMeasuresPoint(const Vector3d & point)1167 void Model::setMeasuresPoint(const Vector3d &point)
1168 {
1169   measuresPoint = Vector2d(point.x(), point.y()) ;
1170 }
1171