]> Shamusworld >> Repos - architektonas/blob - src/fileio.cpp
7f1a90a8d08b7e7c634545c0fb78b34dbc1b94c8
[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         WriteObjectToFile(file, (Object *)c);
130         fprintf(file, "END\n");
131         return true;
132 }
133
134
135 /*static*/ bool FileIO::LoadAtnsFile(FILE * file, Container * drawing)
136 {
137         float version;
138
139         fscanf(file, "ARCHITEKTONAS DRAWING V%f", &version);
140
141         if (version == 1.0f)
142                 return LoadVersion1_0(file, drawing);
143         else if (version == 1.1f)
144                 return LoadVersion1_1(file, drawing);
145
146 //printf("LoadAtnsFile: Could not locate version! (version=%f)\n", version);
147         return false;
148 }
149
150
151 /*static*/ bool FileIO::LoadVersion1_0(FILE * file, Container * drawing)
152 {
153         // Approach: read each object in the file, one by one. If the object is a
154         // Container, add objects to it until an "endContainer" marker is found.
155         // This will require a stack to maintain the current Container.
156         std::vector<Container *> containerStack;
157         Container * currentTopContainer = drawing;
158
159         while (!feof(file))
160         {
161                 // Reconstruct the object
162                 Object * obj = GetObjectFromFile(file);
163
164                 // objectFileType is set in GetObjectFromFile()...
165                 if (objectFileType == OTFObject)
166                 {
167                         if (obj == NULL)
168                                 return false;
169
170                         currentTopContainer->objects.push_back(obj);
171 //printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
172
173                         // If the object is a container, push current TLC on the stack and
174                         // set it as the new TLC
175                         if (obj->type == OTContainer)
176                         {
177                                 containerStack.push_back(currentTopContainer);
178                                 currentTopContainer = (Container *)obj;
179                         }
180                 }
181                 else if (objectFileType == OTFContainerEnd)
182                 {
183                         // Container is done, so pop the stack to get back the previous TLC
184                         currentTopContainer = containerStack.back();
185                         containerStack.pop_back();
186                 }
187                 else if (objectFileType == OTFEndOfFile)
188                 {
189 //printf("Load: container size = %li\n", drawing->objects.size());
190                         return true;
191                 }
192         }
193
194         return false;
195 }
196
197
198 /*static*/ bool FileIO::LoadVersion1_1(FILE * file, Container * drawing)
199 {
200         // Approach: read each object in the file, one by one. If the object is a
201         // Container, add objects to it until an "endContainer" marker is found.
202         // This will require a stack to maintain the current Container.
203         std::vector<Container *> containerStack;
204         Container * currentTopContainer = drawing;
205
206         while (!feof(file))
207         {
208                 // Reconstruct the object (extended format!)
209                 Object * obj = GetObjectFromFile(file, true);
210
211                 // objectFileType is set in GetObjectFromFile()...
212                 if (objectFileType == OTFObject)
213                 {
214                         if (obj == NULL)
215                                 return false;
216
217                         currentTopContainer->objects.push_back(obj);
218 //printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
219
220                         // If the object is a container, push current TLC on the stack and
221                         // set it as the new TLC
222                         if (obj->type == OTContainer)
223                         {
224                                 containerStack.push_back(currentTopContainer);
225                                 currentTopContainer = (Container *)obj;
226                         }
227                 }
228                 else if (objectFileType == OTFContainerEnd)
229                 {
230                         // Container is done, so pop the stack to get back the previous TLC
231                         currentTopContainer = containerStack.back();
232                         containerStack.pop_back();
233                 }
234                 else if (objectFileType == OTFEndOfFile)
235                 {
236 //printf("Load: container size = %li\n", drawing->objects.size());
237                         return true;
238                 }
239         }
240
241         return false;
242 }
243
244
245 /*static*/ Object * FileIO::GetObjectFromFile(FILE * file, bool extended/*= false*/)
246 {
247         char buffer[256];
248         int foundLayer = 0;
249         /*int num =*/ fscanf(file, "%s", buffer);
250         Object * obj = NULL;
251         objectFileType = OTFObject;
252 //printf("FileIO: fscanf returned %i, buffer = \"%s\"\n", num, buffer);
253
254 // The following fugliness is for troubleshooting. Can remove later.
255         if ((strcmp(buffer, "END") != 0) && (strcmp(buffer, "ENDCONTAINER") != 0))
256 {
257 errno = 0;
258                 /*num =*/ fscanf(file, " %i ", &foundLayer);
259 //printf("FileIO: fscanf returned %i, foundLayer = %i\n", num, foundLayer);
260 if (errno)
261 {
262         if (errno == EAGAIN)
263                 printf("EAGAIN\n");
264         else if (errno == EBADF)
265                 printf("EBADF\n");
266         else if (errno == EILSEQ)
267                 printf("EILSEQ\n");
268         else if (errno == EINTR)
269                 printf("EINTR\n");
270         else if (errno == EINVAL)
271                 printf("EINVAL\n");
272         else if (errno == ENOMEM)
273                 printf("ENOMEM\n");
274         else if (errno == ERANGE)
275                 printf("ERANGE\n");
276         else
277                 printf("errno = %i\n", errno);
278 }
279 }
280
281         // Need to add pen attributes as well... do that for v1.1 :-P
282         if (strcmp(buffer, "LINE") == 0)
283         {
284                 Point p1, p2;
285                 fscanf(file, "(%lf,%lf) (%lf,%lf)", &p1.x, &p1.y, &p2.x, &p2.y);
286                 obj = (Object *)new Line(p1, p2);
287         }
288         else if (strcmp(buffer, "CIRCLE") == 0)
289         {
290                 Point p;
291                 double r;
292                 fscanf(file, "(%lf,%lf) %lf", &p.x, &p.y, &r);
293                 obj = (Object *)new Circle(p, r);
294         }
295         else if (strcmp(buffer, "ARC") == 0)
296         {
297                 Point p;
298                 double r, a1, a2;
299                 fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &p.x, &p.y, &r, &a1, &a2);
300                 obj = (Object *)new Arc(p, r, a1, a2);
301         }
302         else if (strcmp(buffer, "DIMENSION") == 0)
303         {
304                 Point p1, p2;
305                 DimensionType type;
306                 fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &p1.x, &p1.y, &p2.x, &p2.y, (int *)&type);
307                 obj = (Object *)new Dimension(p1, p2, type);
308         }
309         else if (strcmp(buffer, "CONTAINER") == 0)
310         {
311                 obj = (Object *)new Container();
312 //              objectFileType = OTFContainer;
313         }
314         else if (strcmp(buffer, "ENDCONTAINER") == 0)
315         {
316                 objectFileType = OTFContainerEnd;
317         }
318         else if (strcmp(buffer, "END") == 0)
319         {
320                 objectFileType = OTFEndOfFile;
321         }
322
323         if (obj != NULL)
324         {
325                 obj->layer = foundLayer;
326
327                 if (extended && (obj->type != OTContainer))
328                 {
329                         fscanf(file, " (%i, %f, %i)", &obj->color, &obj->thickness, &obj->style);
330                 }
331         }
332
333         return obj;
334 }
335
336
337 /*static*/ bool FileIO::WriteObjectToFile(FILE * file, Object * obj)
338 {
339         // Sanity check
340         if (obj == NULL)
341                 return false;
342
343         switch (obj->type)
344         {
345         case OTLine:
346                 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);
347                 break;
348         case OTCircle:
349                 fprintf(file, "CIRCLE %i (%lf,%lf) %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0]);
350                 break;
351         case OTEllipse:
352                 break;
353         case OTArc:
354                 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]);
355                 break;
356         case OTPolygon:
357                 break;
358         case OTDimension:
359 //              fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, dimensionType);
360                 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);
361                 break;
362         case OTSpline:
363                 break;
364         case OTText:
365                 fprintf(file, "TEXT %i (%lf,%lf) \"%s\"", obj->layer, obj->p[0].x, obj->p[0].y, ((Text *)obj)->s.c_str());
366                 break;
367         case OTContainer:
368         {
369                 Container * c = (Container *)obj;
370
371                 if (c->topLevel == false)
372                         fprintf(file, "CONTAINER %i\n", obj->layer);
373
374                 std::vector<void *>::iterator i;
375
376                 for(i=c->objects.begin(); i!=c->objects.end(); i++)
377                         WriteObjectToFile(file, (Object *)*i);
378
379                 if (c->topLevel == false)
380                         fprintf(file, "ENDCONTAINER\n");
381
382                 break;
383         }
384         default:
385                 break;
386         }
387
388         if (obj->type != OTContainer)
389                 fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style);
390
391         return true;
392 }
393