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