]> Shamusworld >> Repos - architektonas/blob - src/fileio.cpp
1002dca87fecf22b79cbb2883253e9660fe6c34b
[architektonas] / src / fileio.cpp
1 //
2 // fileio.cpp: Architektonas file save/load support
3 //
4 // Part of the Architektonas Project
5 // (C) 2013 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  02/20/2013  Created this file
13 //
14
15 #include "fileio.h"
16
17 //#include <stdio.h>
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <vector>
22 #include "structs.h"
23
24 /*
25 How to handle connected objects
26 -------------------------------
27
28 Every Object has a vector<Object *> which enumerates all Objects connected to
29 the one we're looking at. So it looks like we'll have to take a two pass
30 approach to loading and saving.
31
32 Basically, in the saving case, first we write out all objects, keeping a
33 pointer-to-index-number record. Second, we loop through all the objects we
34 wrote out, writing connection lists. Format (indices are 1-based):
35
36 CONNECTIONS
37 1: 12 3
38 3: 1
39 12: 1
40 ENDCONNECTIONS
41
42 In the reading case, we do pretty much the same: we construct pointer-to-index-
43 number list, then read the connection list. The PTIN connects the index to the
44 Object pointers we've created. Then we simply call the Object's Connect()
45 function to connect the objects.
46
47 Small problem though: How does a Dimension know which points on a Line it's
48 connected to? This approach tells the Dimension it's connected to the Line and
49 the Line that it's connected to the Dimension, but not which points.
50
51 How to handle them then? Do we list the point with the Object pointed at? A
52 Line can contain an infinite number of points to connect with besides its
53 endpoints.
54
55 So with each connection Object in the vector, there would also have to be a
56 corresponding point to go with it, that would be gotten from the other Object's
57 Connect() function. Or, instead of a point, a parameter value?
58
59 Doing that, with the Line and a parameter "t", if t == 0 we have endpoint 1.
60 if t == 1, then we have endpoint 2. With a Circle, the parameter is a number
61 between 0 and 1 (scaled to 0 to 2π). With an Arc, the parameter goes from 0 to
62 1, 0 being enpoint 1 and 1 being endpoint 2.
63
64 How does this work for moving objects that are connected? Again, with the Line
65 and Dimension. The Line's connections looks like this:
66
67 Object *: dim1, t = 0
68 Object *: dim1, t = 1
69
70 Dimension looks like this:
71
72 Object *: line1, t = 0
73 Object *: line1, t = 1
74
75 For Dimensions, it can query the connected object (if any) using something like
76 GetPointForParameter(). That way it can figure out where its endpoints are. If
77 there is no connected point, then it uses its internal point.
78
79
80 Dimensions are special cases of lines: They have exactly *two* points and none
81 in between. Therefore, the Dimension object only needs to have two points. But
82 those points can be connected to multiple objects. The can also be connected to
83 no points/Objects too.
84
85 How to describe them and their connections (or lack thereof)?
86
87 Would have to be a 2nd pass, after all objects have been written out in order.
88 Then you could do something like:
89
90 DIMCONNECTIONS
91 8 (the Dimension #): 1 (the Object # for point 1) 1 (the Object # for point 2)
92 ENDDIMCONNECTIONS
93
94
95
96 Connection attributes: E.g., between a Line a Circle, it can be tangent,
97 perpendicular, or an arbitrary angle. How to encode that information? It's not
98 intrinsic to either the Line or the Circle, but is a function of the
99 relationship between them by virtue of their connection.
100
101
102 OTHER CONSIDERATIONS:
103 ---------------------
104
105   - Need to figure out how to store the Layer list (should layer list be optional?)
106   - Need to figure out how to store the Block list and blocks
107
108
109 */
110
111 //enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
112 //      OTFDimension, OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
113 enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFObject, OTFEndOfFile };
114
115
116 // Instantiate class variables
117 /*static*/ int FileIO::objectFileType = OTFObject;
118
119
120 /*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * c)
121 {
122         /* Approach: loop through the container, doing a depth-first traversal. Any
123            extra containers found are looped through until there aren't any more
124            down, then ordinary objects are described. This can be handled by a
125            virtual Object function that reports the object by itself if it's a non-
126            Container, otherwise it enumerates all objects within itself. */
127
128         fprintf(file, "ARCHITEKTONAS DRAWING V1.1\n");
129 #if 0
130         object->Enumerate(file);
131 #else
132         WriteObjectToFile(file, (Object *)c);
133 #endif
134         fprintf(file, "END\n");
135         return true;
136 }
137
138
139 /*static*/ bool FileIO::LoadAtnsFile(FILE * file, Container * drawing)
140 {
141         float version;
142
143         fscanf(file, "ARCHITEKTONAS DRAWING V%f", &version);
144
145 //printf("Load: version = %f\n", version);
146 //      if (version != 1.0)
147 //              return false;
148
149         if (version == 1.0)
150                 return LoadVersion1_0(file, drawing);
151         else if (version == 1.1)
152                 return LoadVersion1_1(file, drawing);
153
154         return false;
155         /* Approach: read each object in the file, one by one. If the object is a
156            Container, add objects to it until an "endContainer" marker is found.
157            This will require a stack to maintain the current Container. */
158
159 #if 0
160         std::vector<Container *> containerStack;
161         Container * currentTopContainer = drawing;//new Container(Vector(0, 0));
162         Object * object;
163 //      ObjectType objectType;
164         int objectType;
165
166         while (!feof(file))
167         {
168                 if (FileIO::GetObjectFromFile(file, currentTopContainer, &object, &objectType) == false)
169                         return false;
170
171                 // object->type down below can be replaced with objType.
172                 // Above could be: bool FileIO::GetObjectFromFile(FILE *, Object *,
173                 // ObjectType *); where the return value tells if it's a valid object,
174                 // Object * returns the reconstructed object and ObjectType * returns
175                 // the object type.
176
177                 if (objectType == OTFEndOfFile)
178                 {
179 //printf("Load: container size = %li\n", drawing->objects.size());
180                         return true;
181                 }
182                 else if (objectType == OTFContainer)
183                 {
184                         containerStack.push_back(currentTopContainer);
185                         currentTopContainer = new Container(Vector(0, 0), currentTopContainer);
186                 }
187                 else if (objectType == OTFContainerEnd)
188                 {
189                         Container * containerToAdd = currentTopContainer;
190                         currentTopContainer = containerStack.back();
191                         containerStack.pop_back();
192                         currentTopContainer->Add(containerToAdd);
193                 }
194                 else
195                 {
196                         currentTopContainer->Add(object);
197 //printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
198                 }
199         }
200
201         return false;
202 #else
203         return false;
204 #endif
205 }
206
207
208 /*static*/ bool FileIO::LoadVersion1_0(FILE * file, Container * drawing)
209 {
210         // Approach: read each object in the file, one by one. If the object is a
211         // Container, add objects to it until an "endContainer" marker is found.
212         // This will require a stack to maintain the current Container.
213         std::vector<Container *> containerStack;
214         Container * currentTopContainer = drawing;
215
216         while (!feof(file))
217         {
218                 // Reconstruct the object
219                 Object * obj = GetObjectFromFile(file);
220
221                 // objectFileType is set in GetObjectFromFile()...
222                 if (objectFileType == OTFObject)
223                 {
224                         if (obj == NULL)
225                                 return false;
226
227                         currentTopContainer->objects.push_back(obj);
228 //printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
229
230                         // If the object is a container, push current TLC on the stack and
231                         // set it as the new TLC
232                         if (obj->type == OTContainer)
233                         {
234                                 containerStack.push_back(currentTopContainer);
235                                 currentTopContainer = (Container *)obj;
236                         }
237                 }
238                 else if (objectFileType == OTFContainerEnd)
239                 {
240                         // Container is done, so pop the stack to get back the previous TLC
241                         currentTopContainer = containerStack.back();
242                         containerStack.pop_back();
243                 }
244                 else if (objectFileType == OTFEndOfFile)
245                 {
246 //printf("Load: container size = %li\n", drawing->objects.size());
247                         return true;
248                 }
249         }
250
251         return false;
252 }
253
254
255 /*static*/ bool FileIO::LoadVersion1_1(FILE * file, Container * drawing)
256 {
257         // Approach: read each object in the file, one by one. If the object is a
258         // Container, add objects to it until an "endContainer" marker is found.
259         // This will require a stack to maintain the current Container.
260         std::vector<Container *> containerStack;
261         Container * currentTopContainer = drawing;
262
263         while (!feof(file))
264         {
265                 // Reconstruct the object (extended format!)
266                 Object * obj = GetObjectFromFile(file, true);
267
268                 // objectFileType is set in GetObjectFromFile()...
269                 if (objectFileType == OTFObject)
270                 {
271                         if (obj == NULL)
272                                 return false;
273
274                         currentTopContainer->objects.push_back(obj);
275 //printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
276
277                         // If the object is a container, push current TLC on the stack and
278                         // set it as the new TLC
279                         if (obj->type == OTContainer)
280                         {
281                                 containerStack.push_back(currentTopContainer);
282                                 currentTopContainer = (Container *)obj;
283                         }
284                 }
285                 else if (objectFileType == OTFContainerEnd)
286                 {
287                         // Container is done, so pop the stack to get back the previous TLC
288                         currentTopContainer = containerStack.back();
289                         containerStack.pop_back();
290                 }
291                 else if (objectFileType == OTFEndOfFile)
292                 {
293 //printf("Load: container size = %li\n", drawing->objects.size());
294                         return true;
295                 }
296         }
297
298         return false;
299 }
300
301
302 /*static*/ Object * FileIO::GetObjectFromFile(FILE * file, bool extended/*= false*/)
303 {
304         char buffer[256];
305         int foundLayer = 0;
306         /*int num =*/ fscanf(file, "%s", buffer);
307         Object * obj = NULL;
308         objectFileType = OTFObject;
309 //printf("FileIO: fscanf returned %i, buffer = \"%s\"\n", num, buffer);
310
311 // The following fugliness is for troubleshooting. Can remove later.
312         if ((strcmp(buffer, "END") != 0) && (strcmp(buffer, "ENDCONTAINER") != 0))
313 {
314 errno = 0;
315                 /*num =*/ fscanf(file, " %i ", &foundLayer);
316 //printf("FileIO: fscanf returned %i, foundLayer = %i\n", num, foundLayer);
317 if (errno)
318 {
319         if (errno == EAGAIN)
320                 printf("EAGAIN\n");
321         else if (errno == EBADF)
322                 printf("EBADF\n");
323         else if (errno == EILSEQ)
324                 printf("EILSEQ\n");
325         else if (errno == EINTR)
326                 printf("EINTR\n");
327         else if (errno == EINVAL)
328                 printf("EINVAL\n");
329         else if (errno == ENOMEM)
330                 printf("ENOMEM\n");
331         else if (errno == ERANGE)
332                 printf("ERANGE\n");
333         else
334                 printf("errno = %i\n", errno);
335 }
336 }
337
338         // Need to add pen attributes as well... do that for v1.1 :-P
339         if (strcmp(buffer, "LINE") == 0)
340         {
341                 Point p1, p2;
342                 fscanf(file, "(%lf,%lf) (%lf,%lf)", &p1.x, &p1.y, &p2.x, &p2.y);
343                 obj = (Object *)new Line(p1, p2);
344         }
345         else if (strcmp(buffer, "CIRCLE") == 0)
346         {
347                 Point p;
348                 double r;
349                 fscanf(file, "(%lf,%lf) %lf", &p.x, &p.y, &r);
350                 obj = (Object *)new Circle(p, r);
351         }
352         else if (strcmp(buffer, "ARC") == 0)
353         {
354                 Point p;
355                 double r, a1, a2;
356                 fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &p.x, &p.y, &r, &a1, &a2);
357                 obj = (Object *)new Arc(p, r, a1, a2);
358         }
359         else if (strcmp(buffer, "DIMENSION") == 0)
360         {
361                 Point p1, p2;
362                 DimensionType type;
363                 fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &p1.x, &p1.y, &p2.x, &p2.y, (int *)&type);
364                 obj = (Object *)new Dimension(p1, p2, type);
365         }
366         else if (strcmp(buffer, "CONTAINER") == 0)
367         {
368                 obj = (Object *)new Container();
369 //              objectFileType = OTFContainer;
370         }
371         else if (strcmp(buffer, "ENDCONTAINER") == 0)
372         {
373                 objectFileType = OTFContainerEnd;
374         }
375         else if (strcmp(buffer, "END") == 0)
376         {
377                 objectFileType = OTFEndOfFile;
378         }
379
380         if (obj != NULL)
381         {
382                 obj->layer = foundLayer;
383
384                 if (extended && (obj->type != OTContainer))
385                 {
386                         fscanf(file, " (%i, %f, %i)", &obj->color, &obj->thickness, &obj->style);
387                 }
388         }
389
390         return obj;
391 }
392
393
394 /*static*/ bool FileIO::WriteObjectToFile(FILE * file, Object * obj)
395 {
396         // Sanity check
397         if (obj == NULL)
398                 return false;
399
400         switch (obj->type)
401         {
402         case OTLine:
403                 fprintf(file, "LINE %i (%lf,%lf) (%lf,%lf)", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y);
404                 break;
405         case OTCircle:
406                 fprintf(file, "CIRCLE %i (%lf,%lf) %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0]);
407                 break;
408         case OTEllipse:
409                 break;
410         case OTArc:
411                 fprintf(file, "ARC %i (%lf,%lf) %lf, %lf, %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0], obj->angle[0], obj->angle[1]);
412                 break;
413         case OTPolygon:
414                 break;
415         case OTDimension:
416 //              fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, dimensionType);
417                 fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y, ((Dimension *)obj)->subtype);
418                 break;
419         case OTSpline:
420                 break;
421         case OTText:
422                 fprintf(file, "TEXT %i (%lf,%lf) \"%s\"", obj->layer, obj->p[0].x, obj->p[0].y, ((Text *)obj)->s.c_str());
423                 break;
424         case OTContainer:
425         {
426                 Container * c = (Container *)obj;
427
428                 if (c->topLevel == false)
429                         fprintf(file, "CONTAINER %i\n", obj->layer);
430
431                 std::vector<void *>::iterator i;
432
433                 for(i=c->objects.begin(); i!=c->objects.end(); i++)
434                         WriteObjectToFile(file, (Object *)*i);
435
436                 if (c->topLevel == false)
437                         fprintf(file, "ENDCONTAINER\n");
438
439                 break;
440         }
441         default:
442                 break;
443         }
444
445         if (obj->type != OTContainer)
446                 fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style);
447
448         return true;
449 }
450