src/ldtypes.cpp

changeset 183
f1b8cb53d2a2
child 185
6fea53f1ffc2
equal deleted inserted replaced
182:9374fea8f77f 183:f1b8cb53d2a2
1 /*
2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013 Santeri 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 "common.h"
20 #include "ldtypes.h"
21 #include "file.h"
22 #include "misc.h"
23 #include "gui.h"
24
25 char const* g_saObjTypeNames[] = {
26 "subfile",
27 "radial",
28 "quadrilateral",
29 "triangle",
30 "line",
31 "condline",
32 "vertex",
33 "bfc",
34 "comment",
35 "unknown",
36 "empty",
37 "unidentified",
38 };
39
40 // Should probably get rid of this array sometime
41 char const* g_saObjTypeIcons[] = {
42 "subfile",
43 "radial",
44 "quad",
45 "triangle",
46 "line",
47 "condline",
48 "vertex",
49 "bfc",
50 "comment",
51 "error",
52 "empty",
53 "error",
54 };
55
56 // =============================================================================
57 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
58 // =============================================================================
59 // LDObject constructors
60 LDObject::LDObject () {
61 qObjListEntry = null;
62 parent = null;
63 m_hidden = false;
64 }
65
66 LDGibberish::LDGibberish (str _zContent, str _zReason) {
67 zContents = _zContent;
68 zReason = _zReason;
69 }
70
71 // =============================================================================
72 str LDComment::getContents () {
73 return fmt ("0 %s", text.chars ());
74 }
75
76 str LDSubfile::getContents () {
77 str val = fmt ("1 %d %s ", dColor, vPosition.stringRep (false).chars ());
78 val += mMatrix.stringRep ();
79 val += ' ';
80 val += zFileName;
81 return val;
82 }
83
84 str LDLine::getContents () {
85 str val = fmt ("2 %d", dColor);
86
87 for (ushort i = 0; i < 2; ++i)
88 val.appendformat (" %s", vaCoords[i].stringRep (false).chars ());
89
90 return val;
91 }
92
93 str LDTriangle::getContents () {
94 str val = fmt ("3 %d", dColor);
95
96 for (ushort i = 0; i < 3; ++i)
97 val.appendformat (" %s", vaCoords[i].stringRep (false).chars ());
98
99 return val;
100 }
101
102 str LDQuad::getContents () {
103 str val = fmt ("4 %d", dColor);
104
105 for (ushort i = 0; i < 4; ++i)
106 val.appendformat (" %s", vaCoords[i].stringRep (false).chars ());
107
108 return val;
109 }
110
111 str LDCondLine::getContents () {
112 str val = fmt ("5 %d", dColor);
113
114 // Add the coordinates
115 for (ushort i = 0; i < 4; ++i)
116 val.appendformat (" %s", vaCoords[i].stringRep (false).chars ());
117
118 return val;
119 }
120
121 str LDGibberish::getContents () {
122 return zContents;
123 }
124
125 str LDVertex::getContents () {
126 return fmt ("0 !LDFORGE VERTEX %d %s", dColor, vPosition.stringRep (false).chars());
127 }
128
129 str LDEmpty::getContents () {
130 return str ();
131 }
132
133 // =============================================================================
134 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
135 // =============================================================================
136 const char* LDBFC::statements[] = {
137 "CERTIFY CCW",
138 "CCW",
139 "CERTIFY CW",
140 "CW",
141 "NOCERTIFY",
142 "INVERTNEXT",
143 };
144
145 str LDBFC::getContents () {
146 return fmt ("0 BFC %s", LDBFC::statements[type]);
147 }
148
149 // =============================================================================
150 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
151 // =============================================================================
152 vector<LDTriangle*> LDQuad::splitToTriangles () {
153 // Create the two triangles based on this quadrilateral:
154 // 0---3 0---3 3
155 // | | | / /|
156 // | | = | / / |
157 // | | |/ / |
158 // 1---2 1 1---2
159 LDTriangle* tri1 = new LDTriangle;
160 tri1->vaCoords[0] = vaCoords[0];
161 tri1->vaCoords[1] = vaCoords[1];
162 tri1->vaCoords[2] = vaCoords[3];
163
164 LDTriangle* tri2 = new LDTriangle;
165 tri2->vaCoords[0] = vaCoords[1];
166 tri2->vaCoords[1] = vaCoords[2];
167 tri2->vaCoords[2] = vaCoords[3];
168
169 // The triangles also inherit the quad's color
170 tri1->dColor = tri2->dColor = dColor;
171
172 vector<LDTriangle*> triangles;
173 triangles.push_back (tri1);
174 triangles.push_back (tri2);
175 return triangles;
176 }
177
178 // =============================================================================
179 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
180 // =============================================================================
181 void LDObject::replace (LDObject* replacement) {
182 // Replace all instances of the old object with the new object
183 for (LDObject*& obj : g_curfile->m_objs)
184 if (obj == this)
185 obj = replacement;
186
187 // Remove the old object
188 delete this;
189 }
190
191 // =============================================================================
192 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
193 // =============================================================================
194 void LDObject::swap (LDObject* other) {
195 for (LDObject*& obj : g_curfile->m_objs) {
196 if (obj == this)
197 obj = other;
198 else if (obj == other)
199 obj = this;
200 }
201 }
202
203 LDLine::LDLine (vertex v1, vertex v2) {
204 vaCoords[0] = v1;
205 vaCoords[1] = v2;
206 }
207
208 LDObject::~LDObject () {
209 // Remove this object from the selection array if it is there.
210 for (ulong i = 0; i < g_win->sel ().size(); ++i)
211 if (g_win->sel ()[i] == this)
212 g_win->sel ().erase (g_win->sel ().begin() + i);
213 }
214
215 // =============================================================================
216 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
217 // =============================================================================
218 static void transformObject (LDObject* obj, matrix<3> transform, vertex pos, short parentcolor) {
219 switch (obj->getType()) {
220 case LDObject::Line:
221 case LDObject::CondLine:
222 case LDObject::Triangle:
223 case LDObject::Quad:
224 for (short i = 0; i < obj->vertices (); ++i)
225 obj->vaCoords[i].transform (transform, pos);
226 break;
227
228 case LDObject::Subfile:
229 {
230 LDSubfile* ref = static_cast<LDSubfile*> (obj);
231
232 matrix<3> newMatrix = transform * ref->mMatrix;
233 ref->vPosition.transform (transform, pos);
234 ref->mMatrix = newMatrix;
235 }
236 break;
237
238 default:
239 break;
240 }
241
242 if (obj->dColor == maincolor)
243 obj->dColor = parentcolor;
244 }
245
246 // =============================================================================
247 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
248 // =============================================================================
249 vector<LDObject*> LDSubfile::inlineContents (bool bDeepInline, bool bCache) {
250 vector<LDObject*> objs, cache;
251
252 // If we have this cached, just clone that
253 if (bDeepInline && pFile->m_objCache.size ()) {
254 for (LDObject* obj : pFile->m_objCache)
255 objs.push_back (obj->clone ());
256 } else {
257 if (!bDeepInline)
258 bCache = false;
259
260 for (LDObject* obj : pFile->m_objs) {
261 // Skip those without schemantic meaning
262 switch (obj->getType ()) {
263 case LDObject::Comment:
264 case LDObject::Empty:
265 case LDObject::Gibberish:
266 case LDObject::Unidentified:
267 case LDObject::Vertex:
268 continue;
269
270 case LDObject::BFC:
271 // Filter non-INVERTNEXT statements
272 if (static_cast<LDBFC*> (obj)->type != LDBFC::InvertNext)
273 continue;
274 break;
275
276 default:
277 break;
278 }
279
280 // Got another sub-file reference, inline it if we're deep-inlining. If not,
281 // just add it into the objects normally. Also, we only cache immediate
282 // subfiles and this is not one. Yay, recursion!
283 if (bDeepInline && obj->getType() == LDObject::Subfile) {
284 LDSubfile* ref = static_cast<LDSubfile*> (obj);
285
286 vector<LDObject*> otherobjs = ref->inlineContents (true, false);
287
288 for (LDObject* otherobj : otherobjs) {
289 // Cache this object, if desired
290 if (bCache)
291 cache.push_back (otherobj->clone ());
292
293 objs.push_back (otherobj);
294 }
295 } else {
296 if (bCache)
297 cache.push_back (obj->clone ());
298
299 objs.push_back (obj->clone ());
300 }
301 }
302
303 if (bCache)
304 pFile->m_objCache = cache;
305 }
306
307 // Transform the objects
308 for (LDObject* obj : objs) {
309 // Set the parent now so we know what inlined this.
310 obj->parent = this;
311
312 transformObject (obj, mMatrix, vPosition, dColor);
313 }
314
315 return objs;
316 }
317
318 // =============================================================================
319 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
320 // =============================================================================
321 long LDObject::getIndex (OpenFile* pFile) {
322 for (ulong i = 0; i < pFile->m_objs.size(); ++i)
323 if (pFile->m_objs[i] == this)
324 return i;
325
326 return -1;
327 }
328
329 // =============================================================================
330 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
331 // =============================================================================
332 void LDObject::moveObjects (std::vector<LDObject*> objs, const bool bUp) {
333 // If we move down, we need to iterate the array in reverse order.
334 const long start = bUp ? 0 : (objs.size() - 1);
335 const long end = bUp ? objs.size() : -1;
336 const long incr = bUp ? 1 : -1;
337
338 for (long i = start; i != end; i += incr) {
339 LDObject* obj = objs[i];
340
341 const long lIndex = obj->getIndex (g_curfile),
342 lTarget = lIndex + (bUp ? -1 : 1);
343
344 if ((bUp == true and lIndex == 0) or
345 (bUp == false and lIndex == (long)(g_curfile->m_objs.size() - 1)))
346 {
347 // One of the objects hit the extrema. If this happens, this should be the first
348 // object to be iterated on. Thus, nothing has changed yet and it's safe to just
349 // abort the entire operation.
350 assert (i == start);
351 return;
352 }
353
354 obj->swap (g_curfile->m_objs[lTarget]);
355 }
356 }
357
358 // =============================================================================
359 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
360 // =============================================================================
361 str LDObject::objectListContents (const std::vector<LDObject*>& objs) {
362 bool firstDetails = true;
363 str text = "";
364
365 if (objs.size() == 0)
366 return "nothing"; // :)
367
368 for (long i = 0; i < LDObject::NumTypes; ++i) {
369 LDObject::Type objType = (LDObject::Type) i;
370 ulong objCount = 0;
371
372 for (LDObject* obj : objs)
373 if (obj->getType() == objType)
374 objCount++;
375
376 if (objCount == 0)
377 continue;
378
379 if (!firstDetails)
380 text += ", ";
381
382 str noun = fmt ("%s%s", g_saObjTypeNames[objType], PLURAL (objCount));
383
384 // Plural of "vertex" is "vertices". Stupid English.
385 if (objType == LDObject::Vertex && objCount != 1)
386 noun = "vertices";
387
388 text.appendformat ("%lu %s", objCount, noun.chars ());
389 firstDetails = false;
390 }
391
392 return text;
393 }
394
395 // =============================================================================
396 LDObject* LDObject::topLevelParent () {
397 if (!parent)
398 return null;
399
400 LDObject* it = this;
401
402 while (it->parent)
403 it = it->parent;
404
405 return it;
406 }
407
408
409 // =============================================================================
410 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
411 // =============================================================================
412 void LDObject::move (vertex vVector) { vVector = vVector; /* to shut up GCC */ }
413 void LDEmpty::move (vertex vVector) { vVector = vVector; }
414 void LDBFC::move (vertex vVector) { vVector = vVector; }
415 void LDComment::move (vertex vVector) { vVector = vVector; }
416 void LDGibberish::move (vertex vVector) { vVector = vVector; }
417
418 void LDVertex::move (vertex vVector) {
419 vPosition += vVector;
420 }
421
422 void LDSubfile::move (vertex vVector) {
423 vPosition += vVector;
424 }
425
426 void LDRadial::move (vertex vVector) {
427 vPosition += vVector;
428 }
429
430 void LDLine::move (vertex vVector) {
431 for (short i = 0; i < 2; ++i)
432 vaCoords[i] += vVector;
433 }
434
435 void LDTriangle::move (vertex vVector) {
436 for (short i = 0; i < 3; ++i)
437 vaCoords[i] += vVector;
438 }
439
440 void LDQuad::move (vertex vVector) {
441 for (short i = 0; i < 4; ++i)
442 vaCoords[i] += vVector;
443 }
444
445 void LDCondLine::move (vertex vVector) {
446 for (short i = 0; i < 4; ++i)
447 vaCoords[i] += vVector;
448 }
449
450 // =============================================================================
451 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
452 // =============================================================================
453 static char const* g_saRadialTypeNames[] = {
454 "Circle",
455 "Cylinder",
456 "Disc",
457 "Disc Negative",
458 "Ring",
459 "Cone",
460 null
461 };
462
463 char const* LDRadial::radialTypeName () {
464 return g_saRadialTypeNames[eRadialType];
465 }
466
467 char const* LDRadial::radialTypeName (const LDRadial::Type eType) {
468 return g_saRadialTypeNames[eType];
469 }
470
471 // =============================================================================
472 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
473 // =============================================================================
474 std::vector<LDObject*> LDRadial::decompose (bool bTransform) {
475 std::vector<LDObject*> paObjects;
476
477 for (short i = 0; i < dSegments; ++i) {
478 double x0 = cos ((i * 2 * pi) / dDivisions),
479 x1 = cos (((i + 1) * 2 * pi) / dDivisions),
480 z0 = sin ((i * 2 * pi) / dDivisions),
481 z1 = sin (((i + 1) * 2 * pi) / dDivisions);
482
483 switch (eRadialType) {
484 case LDRadial::Circle:
485 {
486 vertex v0 (x0, 0.0f, z0),
487 v1 (x1, 0.0f, z1);
488
489 if (bTransform) {
490 v0.transform (mMatrix, vPosition);
491 v1.transform (mMatrix, vPosition);
492 }
493
494 LDLine* pLine = new LDLine;
495 pLine->vaCoords[0] = v0;
496 pLine->vaCoords[1] = v1;
497 pLine->dColor = edgecolor;
498 pLine->parent = this;
499
500 paObjects.push_back (pLine);
501 }
502 break;
503
504 case LDRadial::Cylinder:
505 case LDRadial::Ring:
506 case LDRadial::Cone:
507 {
508 double x2, x3, z2, z3;
509 double y0, y1, y2, y3;
510
511 if (eRadialType == LDRadial::Cylinder) {
512 x2 = x1;
513 x3 = x0;
514 z2 = z1;
515 z3 = z0;
516
517 y0 = y1 = 0.0f;
518 y2 = y3 = 1.0f;
519 } else {
520 x2 = x1 * (dRingNum + 1);
521 x3 = x0 * (dRingNum + 1);
522 z2 = z1 * (dRingNum + 1);
523 z3 = z0 * (dRingNum + 1);
524
525 x0 *= dRingNum;
526 x1 *= dRingNum;
527 z0 *= dRingNum;
528 z1 *= dRingNum;
529
530 if (eRadialType == LDRadial::Ring) {
531 y0 = y1 = y2 = y3 = 0.0f;
532 } else {
533 y0 = y1 = 1.0f;
534 y2 = y3 = 0.0f;
535 }
536 }
537
538 vertex v0 (x0, y0, z0),
539 v1 (x1, y1, z1),
540 v2 (x2, y2, z2),
541 v3 (x3, y3, z3);
542
543 if (bTransform) {
544 v0.transform (mMatrix, vPosition);
545 v1.transform (mMatrix, vPosition);
546 v2.transform (mMatrix, vPosition);
547 v3.transform (mMatrix, vPosition);
548 }
549
550 LDQuad* pQuad = new LDQuad;
551 pQuad->vaCoords[0] = v0;
552 pQuad->vaCoords[1] = v1;
553 pQuad->vaCoords[2] = v2;
554 pQuad->vaCoords[3] = v3;
555 pQuad->dColor = dColor;
556 pQuad->parent = this;
557
558 paObjects.push_back (pQuad);
559 }
560 break;
561
562 case LDRadial::Disc:
563 case LDRadial::DiscNeg:
564 {
565 double x2, z2;
566
567 if (eRadialType == LDRadial::Disc) {
568 x2 = z2 = 0.0f;
569 } else {
570 x2 = (x0 >= 0.0f) ? 1.0f : -1.0f;
571 z2 = (z0 >= 0.0f) ? 1.0f : -1.0f;
572 }
573
574 vertex v0 (x0, 0.0f, z0),
575 v1 (x1, 0.0f, z1),
576 v2 (x2, 0.0f, z2);
577
578 if (bTransform) {
579 v0.transform (mMatrix, vPosition);
580 v1.transform (mMatrix, vPosition);
581 v2.transform (mMatrix, vPosition);
582 }
583
584 LDTriangle* pSeg = new LDTriangle;
585 pSeg->vaCoords[0] = v0;
586 pSeg->vaCoords[1] = v1;
587 pSeg->vaCoords[2] = v2;
588 pSeg->dColor = dColor;
589 pSeg->parent = this;
590
591 paObjects.push_back (pSeg);
592 }
593 break;
594
595 default:
596 break;
597 }
598 }
599
600 return paObjects;
601 }
602
603 // =============================================================================
604 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
605 // =============================================================================
606 str LDRadial::getContents () {
607 return fmt ("0 !LDFORGE RADIAL %s %d %d %d %d %s %s",
608 str (radialTypeName()).toupper ().strip (' ').chars (),
609 dColor, dSegments, dDivisions, dRingNum,
610 vPosition.stringRep (false).chars(), mMatrix.stringRep().chars());
611 }
612
613 char const* g_saRadialNameRoots[] = {
614 "edge",
615 "cyli",
616 "disc",
617 "ndis",
618 "ring",
619 "cone",
620 null
621 };
622
623 // =============================================================================
624 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
625 // =============================================================================
626 str LDRadial::makeFileName () {
627 short numer = dSegments,
628 denom = dDivisions;
629
630 // Simplify the fractional part, but the denominator must be at least 4.
631 simplify (numer, denom);
632
633 if (denom < 4) {
634 const short factor = (4 / denom);
635
636 numer *= factor;
637 denom *= factor;
638 }
639
640 // Compose some general information: prefix, fraction, root, ring number
641 str prefix = (dDivisions == 16) ? "" : fmt ("%d/", dDivisions);
642 str frac = fmt ("%d-%d", numer, denom);
643 str root = g_saRadialNameRoots[eRadialType];
644 str ringNum = (eRadialType == Ring || eRadialType == Cone) ? fmt ("%d", dRingNum) : "";
645
646 // Truncate the root if necessary (7-16rin4.dat for instance).
647 // However, always keep the root at least 2 characters.
648 short extra = (~frac + ~ringNum + ~root) - 8;
649 root -= min<short> (max<short> (extra, 0), 2);
650
651 // Stick them all together and return the result.
652 return fmt ("%s%s%s%s", prefix.chars(), frac.chars (), root.chars (), ringNum.chars ());
653 }
654
655 // =============================================================================
656 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
657 // =============================================================================
658 #define CHECK_FOR_OBJ(N) \
659 if (type == LDObject::N) \
660 return new LD##N;
661 LDObject* LDObject::getDefault (const LDObject::Type type) {
662 CHECK_FOR_OBJ (Comment)
663 CHECK_FOR_OBJ (BFC)
664 CHECK_FOR_OBJ (Line)
665 CHECK_FOR_OBJ (CondLine)
666 CHECK_FOR_OBJ (Radial)
667 CHECK_FOR_OBJ (Subfile)
668 CHECK_FOR_OBJ (Triangle)
669 CHECK_FOR_OBJ (Quad)
670 CHECK_FOR_OBJ (Empty)
671 CHECK_FOR_OBJ (BFC)
672 CHECK_FOR_OBJ (Gibberish)
673 CHECK_FOR_OBJ (Vertex)
674 return null;
675 }

mercurial