src/ldObject.cpp

changeset 1147
a26568aa3cce
parent 1146
bb728c124d47
child 1148
96cb15a7611f
equal deleted inserted replaced
1146:bb728c124d47 1147:a26568aa3cce
1 /*
2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013 - 2017 Teemu Piippo
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 3 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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <assert.h>
20 #include "documentmanager.h"
21 #include "ldObject.h"
22 #include "lddocument.h"
23 #include "miscallenous.h"
24 #include "mainwindow.h"
25 #include "editHistory.h"
26 #include "canvas.h"
27 #include "colors.h"
28 #include "glcompiler.h"
29 #include "linetypes/edgeline.h"
30
31 // List of all LDObjects
32 QMap<qint32, LDObject*> g_allObjects;
33
34 enum { MAX_LDOBJECT_IDS = (1 << 24) };
35
36 #define LDOBJ_DEFAULT_CTOR(T,BASE) \
37 T :: T (Model* model) : \
38 BASE {model} {}
39
40 // =============================================================================
41 // LDObject constructors
42 //
43 LDObject::LDObject (Model* model) :
44 m_isHidden {false},
45 m_isSelected {false},
46 _model {model},
47 m_coords {Origin}
48 {
49 assert(_model != nullptr);
50
51 // Let's hope that nobody goes to create 17 million objects anytime soon...
52 static qint32 nextId = 1; // 0 shalt be null
53 if (nextId < MAX_LDOBJECT_IDS)
54 m_id = nextId++;
55 else
56 m_id = 0;
57
58 if (m_id != 0)
59 g_allObjects[m_id] = this;
60
61 m_randomColor = QColor::fromHsv (rand() % 360, rand() % 256, rand() % 96 + 128);
62 }
63
64 LDSubfileReference::LDSubfileReference (Model* model) :
65 LDMatrixObject (model) {}
66
67 LDOBJ_DEFAULT_CTOR (LDError, LDObject)
68 LDOBJ_DEFAULT_CTOR (LDTriangle, LDObject)
69 LDOBJ_DEFAULT_CTOR (LDQuadrilateral, LDObject)
70 LDOBJ_DEFAULT_CTOR (LDBfc, LDObject)
71 LDOBJ_DEFAULT_CTOR (LDBezierCurve, LDObject)
72
73 LDObject::~LDObject()
74 {
75 // Delete the GL lists
76 if (g_win)
77 g_win->renderer()->forgetObject(this);
78
79 // Remove this object from the list of LDObjects
80 g_allObjects.erase(g_allObjects.find(id()));
81 }
82
83 // =============================================================================
84 //
85 QString LDSubfileReference::asText() const
86 {
87 QString val = format ("1 %1 %2 ", color(), position());
88 val += transformationMatrix().toString();
89 val += ' ';
90 val += fileInfo()->name();
91 return val;
92 }
93
94 // =============================================================================
95 //
96 QString LDTriangle::asText() const
97 {
98 QString val = format ("3 %1", color());
99
100 for (int i = 0; i < 3; ++i)
101 val += format (" %1", vertex (i));
102
103 return val;
104 }
105
106 // =============================================================================
107 //
108 QString LDQuadrilateral::asText() const
109 {
110 QString val = format ("4 %1", color());
111
112 for (int i = 0; i < 4; ++i)
113 val += format (" %1", vertex (i));
114
115 return val;
116 }
117
118 QString LDBezierCurve::asText() const
119 {
120 QString result = format ("0 !LDFORGE BEZIER_CURVE %1", color());
121
122 // Add the coordinates
123 for (int i = 0; i < 4; ++i)
124 result += format (" %1", vertex (i));
125
126 return result;
127 }
128
129 // =============================================================================
130 //
131 QString LDError::asText() const
132 {
133 return contents();
134 }
135
136 // =============================================================================
137 //
138 QString LDBfc::asText() const
139 {
140 return format ("0 BFC %1", statementToString());
141 }
142
143 // =============================================================================
144 //
145 // Swap this object with another.
146 //
147 void LDObject::swap (LDObject* other)
148 {
149 if (model() == other->model())
150 model()->swapObjects (this, other);
151 }
152
153 int LDObject::triangleCount() const
154 {
155 return 0;
156 }
157
158 int LDSubfileReference::triangleCount() const
159 {
160 return fileInfo()->triangleCount();
161 }
162
163 int LDTriangle::triangleCount() const
164 {
165 return 1;
166 }
167
168 int LDQuadrilateral::triangleCount() const
169 {
170 return 2;
171 }
172
173 int LDObject::numVertices() const
174 {
175 return 0;
176 }
177
178 // =============================================================================
179 //
180 LDTriangle::LDTriangle (const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model) :
181 LDObject {model}
182 {
183 setVertex (0, v1);
184 setVertex (1, v2);
185 setVertex (2, v3);
186 }
187
188 // =============================================================================
189 //
190 LDQuadrilateral::LDQuadrilateral (const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4, Model* model) :
191 LDObject {model}
192 {
193 setVertex (0, v1);
194 setVertex (1, v2);
195 setVertex (2, v3);
196 setVertex (3, v4);
197 }
198
199 // =============================================================================
200 //
201 LDBezierCurve::LDBezierCurve(const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model) :
202 LDObject {model}
203 {
204 setVertex (0, v0);
205 setVertex (1, v1);
206 setVertex (2, v2);
207 setVertex (3, v3);
208 }
209
210 // =============================================================================
211 //
212 void LDObject::setDocument (Model* model)
213 {
214 _model = model;
215 }
216
217 // =============================================================================
218 //
219 static void TransformObject (LDObject* obj, Matrix transform, Vertex pos, LDColor parentcolor)
220 {
221 switch (obj->type())
222 {
223 case LDObjectType::EdgeLine:
224 case LDObjectType::ConditionalEdge:
225 case LDObjectType::Triangle:
226 case LDObjectType::Quadrilateral:
227 for (int i = 0; i < obj->numVertices(); ++i)
228 {
229 Vertex v = obj->vertex (i);
230 v.transform (transform, pos);
231 obj->setVertex (i, v);
232 }
233 break;
234
235 case LDObjectType::SubfileReference:
236 {
237 LDSubfileReference* ref = static_cast<LDSubfileReference*> (obj);
238 Matrix newMatrix = transform * ref->transformationMatrix();
239 Vertex newpos = ref->position();
240 newpos.transform (transform, pos);
241 ref->setPosition (newpos);
242 ref->setTransformationMatrix (newMatrix);
243 }
244 break;
245
246 default:
247 break;
248 }
249
250 if (obj->color() == MainColor)
251 obj->setColor (parentcolor);
252 }
253
254 // =============================================================================
255 // -----------------------------------------------------------------------------
256 void LDSubfileReference::inlineContents(Model& model, bool deep, bool render)
257 {
258 Model inlined {this->model()->documentManager()};
259 fileInfo()->inlineContents(inlined, deep, render);
260
261 // Transform the objects
262 for (LDObject* object : inlined)
263 TransformObject(object, transformationMatrix(), position(), color());
264
265 model.merge(inlined);
266 }
267
268 // =============================================================================
269 //
270 LDPolygon* LDObject::getPolygon()
271 {
272 LDObjectType ot = type();
273 int num = (ot == LDObjectType::EdgeLine) ? 2
274 : (ot == LDObjectType::Triangle) ? 3
275 : (ot == LDObjectType::Quadrilateral) ? 4
276 : (ot == LDObjectType::ConditionalEdge) ? 5
277 : 0;
278
279 if (num == 0)
280 return nullptr;
281
282 LDPolygon* data = new LDPolygon;
283 data->id = id();
284 data->num = num;
285 data->color = color().index();
286
287 for (int i = 0; i < data->numVertices(); ++i)
288 data->vertices[i] = vertex (i);
289
290 return data;
291 }
292
293 LDColor LDObject::defaultColor() const
294 {
295 return MainColor;
296 }
297
298 bool LDObject::isColored() const
299 {
300 return true;
301 }
302
303 bool LDObject::isScemantic() const
304 {
305 return true;
306 }
307
308 bool LDObject::hasMatrix() const
309 {
310 return false;
311 }
312
313 // =============================================================================
314 //
315 QList<LDPolygon> LDSubfileReference::inlinePolygons()
316 {
317 QList<LDPolygon> data = fileInfo()->inlinePolygons();
318
319 for (LDPolygon& entry : data)
320 {
321 for (int i = 0; i < entry.numVertices(); ++i)
322 entry.vertices[i].transform (transformationMatrix(), position());
323 }
324
325 return data;
326 }
327
328 // =============================================================================
329 //
330 // Index (i.e. line number) of this object
331 //
332 int LDObject::lineNumber() const
333 {
334 if (model())
335 {
336 for (int i = 0; i < model()->size(); ++i)
337 {
338 if (model()->getObject(i) == this)
339 return i;
340 }
341 }
342
343 return -1;
344 }
345
346 // =============================================================================
347 //
348 // Object after this in the current file
349 //
350 LDObject* LDObject::next() const
351 {
352 return model()->getObject(lineNumber() + 1);
353 }
354
355 // =============================================================================
356 //
357 // Object prior to this in the current file
358 //
359 LDObject* LDObject::previous() const
360 {
361 return model()->getObject(lineNumber() - 1);
362 }
363
364 // =============================================================================
365 //
366 // Is the previous object INVERTNEXT?
367 //
368 bool LDObject::previousIsInvertnext (LDBfc*& ptr)
369 {
370 LDObject* prev = previous();
371
372 if (prev and prev->type() == LDObjectType::Bfc and static_cast<LDBfc*> (prev)->statement() == BfcStatement::InvertNext)
373 {
374 ptr = static_cast<LDBfc*> (prev);
375 return true;
376 }
377
378 return false;
379 }
380
381 // =============================================================================
382 //
383 // Moves this object using the given vertex as a movement List
384 //
385 void LDObject::move (Vertex vect)
386 {
387 if (hasMatrix())
388 {
389 LDMatrixObject* mo = static_cast<LDMatrixObject*> (this);
390 mo->setPosition (mo->position() + vect);
391 }
392 else
393 {
394 for (int i = 0; i < numVertices(); ++i)
395 setVertex (i, vertex (i) + vect);
396 }
397 }
398
399 bool LDObject::isHidden() const
400 {
401 return m_isHidden;
402 }
403
404 void LDObject::setHidden (bool value)
405 {
406 m_isHidden = value;
407 }
408
409 bool LDObject::isSelected() const
410 {
411 return m_isSelected;
412 }
413
414 qint32 LDObject::id() const
415 {
416 return m_id;
417 }
418
419 LDColor LDObject::color() const
420 {
421 return m_color;
422 }
423
424 QColor LDObject::randomColor() const
425 {
426 return m_randomColor;
427 }
428
429 Model* LDObject::model() const
430 {
431 return _model;
432 }
433
434 // =============================================================================
435 //
436 void LDObject::invert() {}
437 void LDBfc::invert() {}
438 void LDError::invert() {}
439
440 // =============================================================================
441 //
442 void LDTriangle::invert()
443 {
444 // Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1.
445 // Thus, we swap 1 and 2.
446 Vertex tmp = vertex (1);
447 setVertex (1, vertex (2));
448 setVertex (2, tmp);
449
450 return;
451 }
452
453 // =============================================================================
454 //
455 void LDQuadrilateral::invert()
456 {
457 // Quad: 0 -> 1 -> 2 -> 3
458 // reversed: 0 -> 3 -> 2 -> 1
459 // Thus, we swap 1 and 3.
460 Vertex tmp = vertex (1);
461 setVertex (1, vertex (3));
462 setVertex (3, tmp);
463 }
464
465 // =============================================================================
466 //
467 void LDSubfileReference::invert()
468 {
469 if (model() == nullptr)
470 return;
471
472 // Check whether subfile is flat
473 int axisSet = (1 << X) | (1 << Y) | (1 << Z);
474 Model model {this->model()->documentManager()};
475 fileInfo()->inlineContents(model, true, false);
476
477 for (LDObject* obj : model.objects())
478 {
479 for (int i = 0; i < obj->numVertices(); ++i)
480 {
481 Vertex const& vrt = obj->vertex (i);
482
483 if (axisSet & (1 << X) and vrt.x() != 0.0)
484 axisSet &= ~(1 << X);
485
486 if (axisSet & (1 << Y) and vrt.y() != 0.0)
487 axisSet &= ~(1 << Y);
488
489 if (axisSet & (1 << Z) and vrt.z() != 0.0)
490 axisSet &= ~(1 << Z);
491 }
492
493 if (axisSet == 0)
494 break;
495 }
496
497 if (axisSet != 0)
498 {
499 // Subfile has all vertices zero on one specific plane, so it is flat.
500 // Let's flip it.
501 Matrix matrixModifier = Matrix::identity;
502
503 if (axisSet & (1 << X))
504 matrixModifier(0, 0) = -1;
505
506 if (axisSet & (1 << Y))
507 matrixModifier(1, 1) = -1;
508
509 if (axisSet & (1 << Z))
510 matrixModifier(2, 2) = -1;
511
512 setTransformationMatrix (transformationMatrix() * matrixModifier);
513 return;
514 }
515
516 // Subfile is not flat. Resort to invertnext.
517 int idx = lineNumber();
518
519 if (idx > 0)
520 {
521 LDBfc* bfc = dynamic_cast<LDBfc*> (previous());
522
523 if (bfc and bfc->statement() == BfcStatement::InvertNext)
524 {
525 // This is prefixed with an invertnext, thus remove it.
526 this->model()->remove(bfc);
527 return;
528 }
529 }
530
531 // Not inverted, thus prefix it with a new invertnext.
532 this->model()->emplaceAt<LDBfc>(idx, BfcStatement::InvertNext);
533 }
534
535 // =============================================================================
536 //
537 void LDBezierCurve::invert()
538 {
539 // A Bézier curve's control points probably need to be, though.
540 Vertex tmp = vertex (1);
541 setVertex (1, vertex (0));
542 setVertex (0, tmp);
543 tmp = vertex (3);
544 setVertex (3, vertex (2));
545 setVertex (2, tmp);
546 }
547
548 // =============================================================================
549 //
550 LDObject* LDObject::fromID(qint32 id)
551 {
552 return g_allObjects.value(id);
553 }
554
555 // =============================================================================
556 //
557 void LDObject::setColor (LDColor color)
558 {
559 changeProperty(&m_color, color);
560 }
561
562 // =============================================================================
563 //
564 // Get a vertex by index
565 //
566 const Vertex& LDObject::vertex (int i) const
567 {
568 return m_coords[i];
569 }
570
571 // =============================================================================
572 //
573 // Set a vertex to the given value
574 //
575 void LDObject::setVertex (int i, const Vertex& vert)
576 {
577 changeProperty(&m_coords[i], vert);
578 }
579
580 LDMatrixObject::LDMatrixObject (Model* model) :
581 LDObject (model),
582 m_position (Origin) {}
583
584 LDMatrixObject::LDMatrixObject (const Matrix& transform, const Vertex& pos, Model* model) :
585 LDObject (model),
586 m_position (pos),
587 m_transformationMatrix (transform) {}
588
589 void LDMatrixObject::setCoordinate (const Axis ax, double value)
590 {
591 Vertex v = position();
592
593 switch (ax)
594 {
595 case X: v.setX (value); break;
596 case Y: v.setY (value); break;
597 case Z: v.setZ (value); break;
598 }
599
600 setPosition (v);
601 }
602
603 const Vertex& LDMatrixObject::position() const
604 {
605 return m_position;
606 }
607
608 // =============================================================================
609 //
610 void LDMatrixObject::setPosition (const Vertex& a)
611 {
612 changeProperty(&m_position, a);
613 }
614
615 // =============================================================================
616 //
617 const Matrix& LDMatrixObject::transformationMatrix() const
618 {
619 return m_transformationMatrix;
620 }
621
622 void LDMatrixObject::setTransformationMatrix (const Matrix& val)
623 {
624 changeProperty(&m_transformationMatrix, val);
625 }
626
627 LDError::LDError (QString contents, QString reason, Model* model) :
628 LDObject (model),
629 m_contents (contents),
630 m_reason (reason) {}
631
632 QString LDError::reason() const
633 {
634 return m_reason;
635 }
636
637 QString LDError::contents() const
638 {
639 return m_contents;
640 }
641
642 QString LDError::fileReferenced() const
643 {
644 return m_fileReferenced;
645 }
646
647 void LDError::setFileReferenced (QString value)
648 {
649 m_fileReferenced = value;
650 }
651
652 LDBfc::LDBfc (const BfcStatement type, Model* model) :
653 LDObject {model},
654 m_statement {type} {}
655
656 BfcStatement LDBfc::statement() const
657 {
658 return m_statement;
659 }
660
661 void LDBfc::setStatement (BfcStatement value)
662 {
663 m_statement = value;
664 }
665
666 QString LDBfc::statementToString() const
667 {
668 return LDBfc::statementToString (statement());
669 }
670
671 QString LDBfc::statementToString (BfcStatement statement)
672 {
673 static const char* statementStrings[] =
674 {
675 "CERTIFY CCW",
676 "CCW",
677 "CERTIFY CW",
678 "CW",
679 "NOCERTIFY",
680 "INVERTNEXT",
681 "CLIP",
682 "CLIP CCW",
683 "CLIP CW",
684 "NOCLIP",
685 };
686
687 if ((int) statement >= 0 and (int) statement < countof (statementStrings))
688 return QString::fromLatin1 (statementStrings[(int) statement]);
689 else
690 return "";
691 }
692
693 Vertex LDBezierCurve::pointAt (qreal t) const
694 {
695 if (t >= 0.0 and t <= 1.0)
696 {
697 Vertex result;
698 result += pow (1.0 - t, 3) * vertex (0);
699 result += (3 * pow (1.0 - t, 2) * t) * vertex (2);
700 result += (3 * (1.0 - t) * pow (t, 2)) * vertex (3);
701 result += pow (t, 3) * vertex (1);
702 return result;
703 }
704 else
705 return Vertex();
706 }
707
708 void LDBezierCurve::rasterize(Model& model, int segments)
709 {
710 QVector<LDPolygon> polygons = rasterizePolygons(segments);
711
712 for (LDPolygon& poly : polygons)
713 {
714 LDEdgeLine* line = model.emplace<LDEdgeLine>(poly.vertices[0], poly.vertices[1]);
715 line->setColor (poly.color);
716 }
717 }
718
719 QVector<LDPolygon> LDBezierCurve::rasterizePolygons(int segments)
720 {
721 QVector<LDPolygon> result;
722 QVector<Vertex> parms;
723 parms.append (pointAt (0.0));
724
725 for (int i = 1; i < segments; ++i)
726 parms.append (pointAt (double (i) / segments));
727
728 parms.append (pointAt (1.0));
729 LDPolygon poly;
730 poly.color = color().index();
731 poly.id = id();
732 poly.num = 2;
733
734 for (int i = 0; i < segments; ++i)
735 {
736 poly.vertices[0] = parms[i];
737 poly.vertices[1] = parms[i + 1];
738 result << poly;
739 }
740
741 return result;
742 }
743
744 LDSubfileReference::LDSubfileReference(LDDocument* reference, const Matrix& transformationMatrix,
745 const Vertex& position, Model* model) :
746 LDMatrixObject {transformationMatrix, position, model},
747 m_fileInfo {reference} {}
748
749 // =============================================================================
750 //
751 LDDocument* LDSubfileReference::fileInfo() const
752 {
753 return m_fileInfo;
754 }
755
756 void LDSubfileReference::setFileInfo (LDDocument* newReferee)
757 {
758 changeProperty(&m_fileInfo, newReferee);
759
760 if (model())
761 model()->recountTriangles();
762
763 // If it's an immediate subfile reference (i.e. this subfile is in an opened document), we need to pre-compile the
764 // GL polygons for the document if they don't exist already.
765 if (newReferee and
766 newReferee->isFrozen() == false and
767 newReferee->polygonData().isEmpty())
768 {
769 newReferee->initializeCachedData();
770 }
771 }
772
773 void LDObject::getVertices (QSet<Vertex>& verts) const
774 {
775 for (int i = 0; i < numVertices(); ++i)
776 verts.insert(vertex(i));
777 }
778
779 void LDSubfileReference::getVertices (QSet<Vertex>& verts) const
780 {
781 verts.unite(fileInfo()->inlineVertices());
782 }
783
784 QString LDObject::objectListText() const
785 {
786 if (numVertices() > 0)
787 {
788 QString result;
789
790 for (int i = 0; i < numVertices(); ++i)
791 {
792 if (i != 0)
793 result += ", ";
794
795 result += vertex(i).toString (true);
796 }
797
798 return result;
799 }
800 else
801 {
802 return typeName();
803 }
804 }
805
806 QString LDError::objectListText() const
807 {
808 return "ERROR: " + asText();
809 }
810
811 QString LDSubfileReference::objectListText() const
812 {
813 QString result = format ("%1 %2, (", fileInfo()->getDisplayName(), position().toString(true));
814
815 for (int i = 0; i < 9; ++i)
816 result += format("%1%2", transformationMatrix().value(i), (i != 8) ? " " : "");
817
818 result += ')';
819 return result;
820 }
821
822 QString LDBfc::objectListText() const
823 {
824 return statementToString();
825 }

mercurial