]> Shamusworld >> Repos - architektonas/blob - src/fileio.cpp
931c5131653b64b89069ec995d67e6305fc25345
[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 "arc.h"
23 #include "circle.h"
24 #include "container.h"
25 #include "dimension.h"
26 #include "line.h"
27
28 /*
29 How to handle connected objects
30 -------------------------------
31
32 Every Object has a vector<Object *> which enumerates all Objects connected to
33 the one we're looking at. So it looks like we'll have to take a two pass
34 approach to loading and saving.
35
36 Basically, in the saving case, first we write out all objects, keeping a
37 pointer-to-index-number record. Second, we loop through all the objects we
38 wrote out, writing connection lists. Format (indices are 1-based):
39
40 CONNECTIONS
41 1: 12 3
42 3: 1
43 12: 1
44 ENDCONNECTIONS
45
46 In the reading case, we do pretty much the same: we construct pointer-to-index-
47 number list, then read the connection list. The PTIN connects the index to the
48 Object pointers we've created. Then we simply call the Object's Connect()
49 function to connect the objects.
50
51 Small problem though: How does a Dimension know which points on a Line it's
52 connected to? This approach tells the Dimension it's connected to the Line and
53 the Line that it's connected to the Dimension, but not which points.
54
55 How to handle them then? Do we list the point with the Object pointed at? A
56 Line can contain an infinite number of points to connect with besides its
57 endpoints.
58
59 So with each connection Object in the vector, there would also have to be a
60 corresponding point to go with it, that would be gotten from the other Object's
61 Connect() function. Or, instead of a point, a parameter value?
62
63 Doing that, with the Line and a parameter "t", if t == 0 we have endpoint 1.
64 if t == 1, then we have endpoint 2. With a Circle, the parameter is a number
65 between 0 and 1 (scaled to 0 to 2π). With an Arc, the parameter goes from 0 to
66 1, 0 being enpoint 1 and 1 being endpoint 2.
67
68 How does this work for moving objects that are connected? Again, with the Line
69 and Dimension. The Line's connections looks like this:
70
71 Object *: dim1, t = 0
72 Object *: dim1, t = 1
73
74 Dimension looks like this:
75
76 Object *: line1, t = 0
77 Object *: line1, t = 1
78
79 For Dimensions, it can query the connected object (if any) using something like
80 GetPointForParameter(). That way it can figure out where its endpoints are. If
81 there is no connected point, then it uses its internal point.
82
83
84 Dimensions are special cases of lines: They have exactly *two* points and none
85 in between. Therefore, the Dimension object only needs to have two points. But
86 those points can be connected to multiple objects. The can also be connected to
87 no points/Objects too.
88
89 How to describe them and their connections (or lack thereof)?
90
91 Would have to be a 2nd pass, after all objects have been written out in order.
92 Then you could do something like:
93
94 DIMCONNECTIONS
95 8 (the Dimension #): 1 (the Object # for point 1) 1 (the Object # for point 2)
96 ENDDIMCONNECTIONS
97
98
99
100 Connection attributes: E.g., between a Line a Circle, it can be tangent,
101 perpendicular, or an arbitrary angle. How to encode that information? It's not
102 intrinsic to either the Line or the Circle, but is a function of the
103 relationship between them by virtue of their connection.
104
105
106 OTHER CONSIDERATIONS:
107 ---------------------
108
109   - Need to figure out how to store the Layer list (should layer list be optional?)
110   - Need to figure out how to store the Block list and blocks
111
112
113 */
114
115 enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc, OTFDimension,
116         OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
117
118
119 /*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * object)
120 {
121         /* Approach: loop through the container, doing a depth-first traversal. Any extra
122            containers found are looped through until there aren't any more down, then
123            ordinary objects are described. This can be handled by a virtual Object function
124            that reports the object by itself if it's a non-Container, otherwise it
125            enumerates all objects within itself. */
126
127         fprintf(file, "ARCHITEKTONAS DRAWING V1.0\n");
128 #if 1
129         object->Enumerate(file);
130         fprintf(file, "END\n");
131         return true;
132 #else
133         return false;
134 #endif
135 }
136
137
138 /*static*/ bool FileIO::LoadAtnsFile(FILE * file, Container * drawing)
139 {
140 //      char buffer[256];
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         /* Approach: read each object in the file, one by one. If the object is a Container,
149            add objects to it until an "endContainer" marker is found. This will require a
150            stack to maintain the current Container. */
151
152 #if 1
153         std::vector<Container *> containerStack;
154         Container * currentTopContainer = drawing;//new Container(Vector(0, 0));
155         Object * object;
156 //      ObjectType objectType;
157         int objectType;
158
159         while (!feof(file))
160         {
161                 if (FileIO::GetObjectFromFile(file, currentTopContainer, &object, &objectType) == false)
162                         return false;
163
164                 // object->type down below can be replaced with objType.
165                 // Above could be: bool FileIO::GetObjectFromFile(FILE *, Object *, ObjectType *);
166                 // where the return value tells if it's a valid object, Object * returns the
167                 // reconstructed object and ObjectType * returns the object type.
168
169                 if (objectType == OTFEndOfFile)
170                 {
171 //printf("Load: container size = %li\n", drawing->objects.size());
172                         return true;
173                 }
174                 else if (objectType == OTFContainer)
175                 {
176                         containerStack.push_back(currentTopContainer);
177                         currentTopContainer = new Container(Vector(0, 0), currentTopContainer);
178                 }
179                 else if (objectType == OTFContainerEnd)
180                 {
181                         Container * containerToAdd = currentTopContainer;
182                         currentTopContainer = containerStack.back();
183                         containerStack.pop_back();
184                         currentTopContainer->Add(containerToAdd);
185                 }
186                 else
187                 {
188                         currentTopContainer->Add(object);
189 //printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
190                 }
191         }
192
193         return false;
194 #else
195         return false;
196 #endif
197 }
198
199
200 /*static*/ bool FileIO::GetObjectFromFile(FILE * file, Object * parent, Object ** object, int * objectType)
201 {
202         char buffer[256];
203         int foundLayer = 0;
204         int num = fscanf(file, "%s", buffer);
205         bool recognized = false;
206         *object = NULL;
207 //printf("FileIO: fscanf returned %i, buffer = \"%s\"\n", num, buffer);
208
209 // The following fugliness is for troubleshooting. Can remove later.
210         if ((strcmp(buffer, "END") != 0) && (strcmp(buffer, "ENDCONTAINER") != 0))
211 {
212 errno = 0;
213                 num = fscanf(file, " %i ", &foundLayer);
214 //printf("FileIO: fscanf returned %i, foundLayer = %i\n", num, foundLayer);
215 if (errno)
216 {
217         if (errno == EAGAIN)
218                 printf("EAGAIN\n");
219         else if (errno == EBADF)
220                 printf("EBADF\n");
221         else if (errno == EILSEQ)
222                 printf("EILSEQ\n");
223         else if (errno == EINTR)
224                 printf("EINTR\n");
225         else if (errno == EINVAL)
226                 printf("EINVAL\n");
227         else if (errno == ENOMEM)
228                 printf("ENOMEM\n");
229         else if (errno == ERANGE)
230                 printf("ERANGE\n");
231         else
232                 printf("errno = %i\n", errno);
233 }
234 }
235
236         if (strcmp(buffer, "LINE") == 0)
237         {
238 //printf("      Found LINE.\n");
239                 recognized = true;
240                 Vector v1, v2;
241                 fscanf(file, "(%lf,%lf) (%lf,%lf)", &v1.x, &v1.y, &v2.x, &v2.y);
242 //printf("      Number of params recognized: %i\n", n);
243                 *object = new Line(v1, v2, parent);
244                 *objectType = OTFLine;
245         }
246         else if (strcmp(buffer, "CIRCLE") == 0)
247         {
248                 recognized = true;
249                 Vector v;
250                 double r;
251                 fscanf(file, "(%lf,%lf) %lf", &v.x, &v.y, &r);
252                 *object = new Circle(v, r, parent);
253                 *objectType = OTFCircle;
254         }
255         else if (strcmp(buffer, "ARC") == 0)
256         {
257                 recognized = true;
258                 Vector v;
259                 double r, a1, a2;
260                 fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &v.x, &v.y, &r, &a1, &a2);
261                 *object = new Arc(v, r, a1, a2, parent);
262                 *objectType = OTFArc;
263         }
264         else if (strcmp(buffer, "DIMENSION") == 0)
265         {
266                 recognized = true;
267                 Vector v1, v2;
268                 DimensionType type;
269                 fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &v1.x, &v1.y, &v2.x, &v2.y, &type);
270                 *object = new Dimension(v1, v2, type, parent);
271                 *objectType = OTFDimension;
272         }
273         else if (strcmp(buffer, "CONTAINER") == 0)
274         {
275                 recognized = true;
276                 *objectType = OTFContainer;
277         }
278         else if (strcmp(buffer, "ENDCONTAINER") == 0)
279         {
280                 recognized = true;
281                 *objectType = OTFContainerEnd;
282         }
283         else if (strcmp(buffer, "END") == 0)
284         {
285                 recognized = true;
286                 *objectType = OTFEndOfFile;
287         }
288
289         if (*object)
290                 (*object)->layer = foundLayer;
291
292         return recognized;
293 }
294