]> Shamusworld >> Repos - architektonas/blob - src/utils.cpp
Further progress on Polylines: Polylines can be selected and moved.
[architektonas] / src / utils.cpp
1 //
2 // utils.cpp: Stuff that's useful to have kicking around, in one spot
3 //
4 // Part of the Architektonas Project
5 // (C) 2020 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // Who  When        What
11 // ---  ----------  ------------------------------------------------------------
12 // JLH  05/01/2015  Created this file
13 //
14
15 #include "utils.h"
16 #include <math.h>
17 #include <string.h>             // For memcpy()
18 #include "geometry.h"
19
20 //
21 // Copy objects in one vector to another, creating copies and placing them in
22 // the other vector. Clearing & etc. of vectors is responsibility of the caller!
23 //
24 void CopyObjects(VPVector & from, VPVector & to)
25 {
26         for(VPVectorIter i=from.begin(); i!=from.end(); i++)
27         {
28                 Object * obj = (Object *)(*i);
29                 Object * newObject = CopyObject(obj);
30                 to.push_back(newObject);
31         }
32 }
33
34 VPVector CopyObjects(VPVector & src)
35 {
36         VPVector copy;
37
38         for(VPVectorIter i=src.begin(); i!=src.end(); i++)
39         {
40                 Object * newObject = CopyObject2((Object *)(*i));
41                 copy.push_back(newObject);
42         }
43
44         return copy;
45 }
46
47 //
48 // Create a copy of the passed in object.
49 //
50 Object * CopyObject(Object * obj)
51 {
52         void * newObject = NULL;
53
54         switch (obj->type)
55         {
56         case OTLine:
57                 newObject = new Line();
58                 memcpy(newObject, obj, sizeof(Line));
59                 break;
60         case OTCircle:
61                 newObject = new Circle();
62                 memcpy(newObject, obj, sizeof(Circle));
63                 break;
64         case OTEllipse:
65                 newObject = new Ellipse();
66                 memcpy(newObject, obj, sizeof(Ellipse));
67                 break;
68         case OTArc:
69                 newObject = new Arc();
70                 memcpy(newObject, obj, sizeof(Arc));
71                 break;
72         case OTDimension:
73                 newObject = new Dimension();
74                 memcpy(newObject, obj, sizeof(Dimension));
75                 break;
76         case OTPolyline:
77                 newObject = new Polyline();
78                 memcpy(newObject, obj, sizeof(Polyline));
79                 ((Polyline *)newObject)->points = ((Polyline *)obj)->points;
80                 break;
81         case OTSpline:
82                 newObject = new Spline();
83                 memcpy(newObject, obj, sizeof(Spline));
84                 break;
85         case OTText:
86                 newObject = new Text();
87                 memcpy(newObject, obj, sizeof(Text));
88                 ((Text *)newObject)->s = ((Text *)obj)->s;
89                 break;
90         case OTContainer:
91                 newObject = new Container();
92 //this won't work...
93 //              memcpy(newObject, obj, sizeof(Line));
94                 ((Container *)newObject)->p[0] = obj->p[0];
95                 ((Container *)newObject)->p[1] = obj->p[1];
96                 ((Container *)newObject)->layer = obj->layer;
97                 CopyObjects(((Container *)obj)->objects, ((Container *)newObject)->objects);
98                 break;
99         default:
100                 break;
101         }
102
103         // Fix objectID
104         if (newObject && (((Object *)newObject)->type != OTContainer))
105                 ((Object *)newObject)->id = Global::objectID++;
106
107         return (Object *)newObject;
108 }
109
110 //
111 // Create a copy of the passed in object.  This version calls the second
112 // version of CopyObjects() (with one parameter and a vector return value).
113 //
114 Object * CopyObject2(Object * obj)
115 {
116         void * newObject = NULL;
117
118         switch (obj->type)
119         {
120         case OTLine:
121                 newObject = new Line();
122                 memcpy(newObject, obj, sizeof(Line));
123                 break;
124
125         case OTCircle:
126                 newObject = new Circle();
127                 memcpy(newObject, obj, sizeof(Circle));
128                 break;
129
130         case OTEllipse:
131                 newObject = new Ellipse();
132                 memcpy(newObject, obj, sizeof(Ellipse));
133                 break;
134
135         case OTArc:
136                 newObject = new Arc();
137                 memcpy(newObject, obj, sizeof(Arc));
138                 break;
139
140         case OTDimension:
141                 newObject = new Dimension();
142                 memcpy(newObject, obj, sizeof(Dimension));
143                 break;
144
145         case OTPolyline:
146                 newObject = new Polyline();
147                 memcpy(newObject, obj, sizeof(Polyline));
148                 ((Polyline *)newObject)->points = ((Polyline *)obj)->points;
149                 break;
150
151         case OTSpline:
152                 newObject = new Spline();
153                 memcpy(newObject, obj, sizeof(Spline));
154                 break;
155
156         case OTText:
157                 newObject = new Text();
158                 memcpy(newObject, obj, sizeof(Text));
159                 ((Text *)newObject)->s = ((Text *)obj)->s;
160                 break;
161
162         case OTContainer:
163                 newObject = new Container();
164                 ((Container *)newObject)->p[0] = obj->p[0];
165                 ((Container *)newObject)->p[1] = obj->p[1];
166                 ((Container *)newObject)->layer = obj->layer;
167                 ((Container *)newObject)->objects = CopyObjects(((Container *)obj)->objects);
168                 break;
169         }
170
171         // Fix objectID
172         if (newObject && (((Object *)newObject)->type != OTContainer))
173                 ((Object *)newObject)->id = Global::objectID++;
174
175         return (Object *)newObject;
176 }
177
178 void MoveSelectedObjectsTo(VPVector & dest, VPVector & from)
179 {
180         VPVectorIter i = from.begin();
181
182         while (i != from.end())
183         {
184                 Object * obj = (Object *)(*i);
185
186                 if (obj->selected)
187                 {
188                         dest.push_back(*i);
189                         from.erase(i);
190                 }
191                 else
192                         i++;
193         }
194 }
195
196 VPVector MoveSelectedObjectsFrom(VPVector & from)
197 {
198         VPVector objects;
199         VPVectorIter i = from.begin();
200
201         while (i != from.end())
202         {
203                 Object * obj = (Object *)(*i);
204
205                 if (obj->selected)
206                 {
207                         objects.push_back(*i);
208                         from.erase(i);
209                 }
210                 else
211                         i++;
212         }
213
214         return objects;
215 }
216
217 //hmm, this won't work, as these are just pointers...
218 //[should work now]
219 void CopySelectedObjectsTo(VPVector & dest, VPVector & from)
220 {
221         for(VPVectorIter i=from.begin(); i!=from.end(); i++)
222         {
223                 Object * obj = (Object *)(*i);
224
225                 if (obj->selected)
226                         dest.push_back(CopyObject2(obj));
227         }
228 }
229
230 VPVector CopySelectedObjects(VPVector & src)
231 {
232         VPVector copy;
233
234         for(VPVectorIter i=src.begin(); i!=src.end(); i++)
235         {
236                 Object * obj = (Object *)(*i);
237
238                 if (obj->selected)
239                         copy.push_back(CopyObject2(obj));
240         }
241
242         return copy;
243 }
244
245 void AddObjectsTo(VPVector & dest, VPVector & from)
246 {
247         for(VPVectorIter i=from.begin(); i!=from.end(); i++)
248                 dest.push_back(*i);
249 }
250
251 void ClearSelected(VPVector & v)
252 {
253         for(VPVectorIter i=v.begin(); i!=v.end(); i++)
254                 ((Object *)(*i))->selected = false;
255 }
256
257 //
258 // Select all *visible* objects.  If an object's layer is invisible, skip it.
259 //
260 void SelectAll(VPVector & v)
261 {
262         for(VPVectorIter i=v.begin(); i!=v.end(); i++)
263         {
264                 Object * obj = (Object *)(*i);
265                 bool visible = !Global::layerHidden[obj->layer];
266                 obj->selected = visible;
267         }
268 }
269
270 //
271 // Recursively go down thru the Container's vectors, deleting all the objects
272 // contained therein.  Once that is done, the main Container can be deleted.
273 // We don't have to worry about the underlying std::vectors, as they have their
274 // own destructors--plus they don't take ownership of objects, which is why we
275 // have to keep track of that stuff ourselves.  :-P  Believe it or not, this is
276 // a Good Thing(TM).  ;-)
277 //
278 void DeleteContents(VPVector & v)
279 {
280         for(VPVectorIter i=v.begin(); i!=v.end(); i++)
281         {
282                 Object * obj = (Object *)(*i);
283
284                 if (obj->type == OTContainer)
285                         DeleteContents(((Container *)obj)->objects);
286
287                 delete obj;
288         }
289
290         v.clear();
291 }
292
293 void DeleteSelectedObjects(VPVector & v)
294 {
295         VPVectorIter i = v.begin();
296
297         while (i != v.end())
298         {
299                 Object * obj = (Object *)(*i);
300
301                 if (obj->selected)
302                 {
303                         delete obj;
304                         v.erase(i);
305                 }
306                 else
307                         i++;
308         }
309 }
310
311 //
312 // This is used to remove selected objects from one container in order to move
313 // them to a different container.
314 //
315 void RemoveSelectedObjects(VPVector & v)
316 {
317         VPVectorIter i = v.begin();
318
319         while (i != v.end())
320         {
321                 Object * obj = (Object *)(*i);
322
323                 if (obj->selected)
324                         v.erase(i);
325                 else
326                         i++;
327         }
328 }
329
330 //
331 // This is used to remove hovered objects from one container in order to delete
332 // them from the same container.
333 //
334 void RemoveHoveredObjects(VPVector & v)
335 {
336         VPVectorIter i = v.begin();
337
338         while (i != v.end())
339         {
340                 Object * obj = (Object *)(*i);
341
342                 if (obj->hovered)
343                         v.erase(i);
344                 else
345                         i++;
346         }
347 }
348
349 void SavePointsFrom(VPVector & v, std::vector<Object> & save)
350 {
351         save.clear();
352         Object o;
353
354         for(VPVectorIter i=v.begin(); i!=v.end(); i++)
355         {
356                 memcpy(&o, (Object *)(*i), sizeof(Object));
357                 save.push_back(o);
358         }
359 }
360
361 void RestorePointsTo(VPVector & v, std::vector<Object> & s)
362 {
363         std::vector<Object>::iterator i;
364         VPVectorIter j;
365
366         for(i=s.begin(), j=v.begin(); i!=s.end(); i++, j++)
367         {
368                 Object * obj2 = (Object *)(*j);
369                 obj2->p[0] = (*i).p[0];
370                 obj2->p[1] = (*i).p[1];
371                 obj2->angle[0] = (*i).angle[0];
372                 obj2->angle[1] = (*i).angle[1];
373 //we don't do this because we want to keep selected & friends from changing
374 //              memcpy(obj2, *j, sizeof(Object));
375         }
376 }
377
378 void RestorePointsTo(VPVector & v, VPVector & s)
379 {
380         for(VPVectorIter i=s.begin(), j=v.begin(); i!=s.end(); i++, j++)
381         {
382                 Object * objS = (Object *)(*i);
383                 Object * objV = (Object *)(*j);
384
385                 if (objV->type == OTContainer)
386                 {
387                         RestorePointsTo(((Container *)objV)->objects, ((Container *)objS)->objects);
388                         return;
389                 }
390
391                 objV->p[0] = objS->p[0];
392                 objV->p[1] = objS->p[1];
393                 objV->angle[0] = objS->angle[0];
394                 objV->angle[1] = objS->angle[1];
395 //we don't do this because we want to keep selected & friends from changing
396 //              memcpy(obj2, *j, sizeof(Object));
397         }
398 }
399
400 //
401 // Translate a single object; it it's a Container, translate all its contents,
402 // including subcontainers.
403 //
404 void TranslateObject(Object * obj, Point delta)
405 {
406         if (obj->type == OTContainer)
407         {
408                 Container * c = (Container *)obj;
409
410                 for(VPVectorIter i=c->objects.begin(); i!=c->objects.end(); i++)
411                         TranslateObject((Object *)*i, delta);
412         }
413         else if (obj->type == OTPolyline)
414                 ((Polyline *)obj)->Translate(delta);
415
416         obj->p[0] += delta;
417         obj->p[1] += delta;
418 }
419
420 /*
421 So we need to make it so that we pick the container's point clicked on, and translate all the other parts *not* clicked on.
422 */
423 void TranslateContainer(Container * c, Point point, Point delta)
424 {
425         if (c->clicked == NULL)
426 //      {
427 //              TranslateObject((Object *)c, delta);
428                 return;
429 //      }
430
431 //static int i=0;
432 //printf("TranslateContainer: boop (%i)\n", i++);
433 //we can set this to "point" and it won't move...
434 //do it *this* way, and non-enumerated clicks will do the right thing
435         Point clickedPoint = point - delta;
436
437         switch (c->clicked->type)
438         {
439         case OTLine:
440                 if (c->clicked->hitPoint[0])
441                         clickedPoint = c->clicked->p[0];
442                 else if (c->clicked->hitPoint[1])
443                         clickedPoint = c->clicked->p[1];
444                 else if (c->clicked->hitObject)
445 //Weirdness: some lines get a midpoint, some don't...
446                         clickedPoint = Geometry::Midpoint((Line *)(c->clicked));
447
448                 break;
449
450         case OTCircle:
451                 if (c->clicked->hitPoint[0])
452                         clickedPoint = c->clicked->p[0];
453 //              else if (c->clicked->hitObject)
454 //                      clickedPoint = point - delta;
455
456                 break;
457
458         case OTArc:
459                 if (c->clicked->hitPoint[0])
460                         clickedPoint = c->clicked->p[0];
461                 else if (c->clicked->hitPoint[1])
462                         clickedPoint = c->clicked->p[0] + (Vector(cos(c->clicked->angle[0]), sin(c->clicked->angle[0])) * c->clicked->radius[0]);
463                 else if (c->clicked->hitPoint[2])
464                         clickedPoint = c->clicked->p[0] + (Vector(cos(c->clicked->angle[0] + c->clicked->angle[1]), sin(c->clicked->angle[0] + c->clicked->angle[1])) * c->clicked->radius[0]);
465 //              else if (c->clicked->hitObject)
466 //                      clickedPoint = point - delta;
467
468                 break;
469
470         case OTDimension:
471                 break;
472
473         case OTText:
474                 break;
475         }
476
477         Point clickedDelta = point - clickedPoint;
478         TranslateObject((Object *)c, clickedDelta);
479 }
480
481 //
482 // Translate all objects in the passed in vector, including Containers and all
483 // the objects they contain.
484 //
485 void TranslateObjects(VPVector & v, Point delta)
486 {
487         for(VPVectorIter i=v.begin(); i!=v.end(); i++)
488         {
489                 Object * obj = (Object *)(*i);
490                 obj->p[0] += delta;
491                 obj->p[1] += delta;
492
493                 if (obj->type == OTContainer)
494                 {
495                         Container * c = (Container *)obj;
496                         TranslateObjects(c->objects, delta);
497                 }
498                 else if (obj->type == OTPolyline)
499                 {
500                         ((Polyline *)obj)->Translate(delta);
501                 }
502         }
503 }
504
505 //
506 // This does not *copy* the objects, it simply flattens out the pointers in the
507 // Container and all sub-Containers.
508 //
509 VPVector Flatten(Container * src)
510 {
511         VPVector flat;
512
513         for(VPVectorIter i=src->objects.begin(); i!=src->objects.end(); i++)
514         {
515                 flat.push_back(*i);
516                 Object * obj = (Object *)(*i);
517
518                 // Recursively add objects to the flat vector, if necessary
519                 if (obj->type == OTContainer)
520                 {
521                         VPVector sub = Flatten((Container *)obj);
522                         flat.insert(flat.end(), sub.begin(), sub.end());
523                 }
524         }
525
526         return flat;
527 }
528
529 //
530 // This does not *copy* the objects, it simply flattens out the pointers in the
531 // vector and all sub-Containers in the vector.
532 //
533 VPVector Flatten(VPVector src)
534 {
535         VPVector flat;
536
537         for(VPVectorIter i=src.begin(); i!=src.end(); i++)
538         {
539                 flat.push_back(*i);
540                 Object * obj = (Object *)(*i);
541
542                 // Recursively add objects to the flat vector, if necessary
543                 if (obj->type == OTContainer)
544                 {
545                         VPVector sub = Flatten(((Container *)obj)->objects);
546                         flat.insert(flat.end(), sub.begin(), sub.end());
547                 }
548         }
549
550         return flat;
551 }