2 // fileio.cpp: Architektonas file save/load support
4 // Part of the Architektonas Project
5 // (C) 2013 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- -------------------------------------------------------------
12 // JLH 02/20/2013 Created this file
24 //#include "container.h"
25 //#include "dimension.h"
30 How to handle connected objects
31 -------------------------------
33 Every Object has a vector<Object *> which enumerates all Objects connected to
34 the one we're looking at. So it looks like we'll have to take a two pass
35 approach to loading and saving.
37 Basically, in the saving case, first we write out all objects, keeping a
38 pointer-to-index-number record. Second, we loop through all the objects we
39 wrote out, writing connection lists. Format (indices are 1-based):
47 In the reading case, we do pretty much the same: we construct pointer-to-index-
48 number list, then read the connection list. The PTIN connects the index to the
49 Object pointers we've created. Then we simply call the Object's Connect()
50 function to connect the objects.
52 Small problem though: How does a Dimension know which points on a Line it's
53 connected to? This approach tells the Dimension it's connected to the Line and
54 the Line that it's connected to the Dimension, but not which points.
56 How to handle them then? Do we list the point with the Object pointed at? A
57 Line can contain an infinite number of points to connect with besides its
60 So with each connection Object in the vector, there would also have to be a
61 corresponding point to go with it, that would be gotten from the other Object's
62 Connect() function. Or, instead of a point, a parameter value?
64 Doing that, with the Line and a parameter "t", if t == 0 we have endpoint 1.
65 if t == 1, then we have endpoint 2. With a Circle, the parameter is a number
66 between 0 and 1 (scaled to 0 to 2π). With an Arc, the parameter goes from 0 to
67 1, 0 being enpoint 1 and 1 being endpoint 2.
69 How does this work for moving objects that are connected? Again, with the Line
70 and Dimension. The Line's connections looks like this:
75 Dimension looks like this:
77 Object *: line1, t = 0
78 Object *: line1, t = 1
80 For Dimensions, it can query the connected object (if any) using something like
81 GetPointForParameter(). That way it can figure out where its endpoints are. If
82 there is no connected point, then it uses its internal point.
85 Dimensions are special cases of lines: They have exactly *two* points and none
86 in between. Therefore, the Dimension object only needs to have two points. But
87 those points can be connected to multiple objects. The can also be connected to
88 no points/Objects too.
90 How to describe them and their connections (or lack thereof)?
92 Would have to be a 2nd pass, after all objects have been written out in order.
93 Then you could do something like:
96 8 (the Dimension #): 1 (the Object # for point 1) 1 (the Object # for point 2)
101 Connection attributes: E.g., between a Line a Circle, it can be tangent,
102 perpendicular, or an arbitrary angle. How to encode that information? It's not
103 intrinsic to either the Line or the Circle, but is a function of the
104 relationship between them by virtue of their connection.
107 OTHER CONSIDERATIONS:
108 ---------------------
110 - Need to figure out how to store the Layer list (should layer list be optional?)
111 - Need to figure out how to store the Block list and blocks
116 enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
117 OTFDimension, OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
120 /*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * object)
122 /* Approach: loop through the container, doing a depth-first traversal. Any extra
123 containers found are looped through until there aren't any more down, then
124 ordinary objects are described. This can be handled by a virtual Object function
125 that reports the object by itself if it's a non-Container, otherwise it
126 enumerates all objects within itself. */
128 fprintf(file, "ARCHITEKTONAS DRAWING V1.0\n");
130 object->Enumerate(file);
131 fprintf(file, "END\n");
139 /*static*/ bool FileIO::LoadAtnsFile(FILE * file, Container * drawing)
144 fscanf(file, "ARCHITEKTONAS DRAWING V%f", &version);
146 //printf("Load: version = %f\n", version);
149 /* Approach: read each object in the file, one by one. If the object is a Container,
150 add objects to it until an "endContainer" marker is found. This will require a
151 stack to maintain the current Container. */
154 std::vector<Container *> containerStack;
155 Container * currentTopContainer = drawing;//new Container(Vector(0, 0));
157 // ObjectType objectType;
162 if (FileIO::GetObjectFromFile(file, currentTopContainer, &object, &objectType) == false)
165 // object->type down below can be replaced with objType.
166 // Above could be: bool FileIO::GetObjectFromFile(FILE *, Object *, ObjectType *);
167 // where the return value tells if it's a valid object, Object * returns the
168 // reconstructed object and ObjectType * returns the object type.
170 if (objectType == OTFEndOfFile)
172 //printf("Load: container size = %li\n", drawing->objects.size());
175 else if (objectType == OTFContainer)
177 containerStack.push_back(currentTopContainer);
178 currentTopContainer = new Container(Vector(0, 0), currentTopContainer);
180 else if (objectType == OTFContainerEnd)
182 Container * containerToAdd = currentTopContainer;
183 currentTopContainer = containerStack.back();
184 containerStack.pop_back();
185 currentTopContainer->Add(containerToAdd);
189 currentTopContainer->Add(object);
190 //printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
201 /*static*/ bool FileIO::GetObjectFromFile(FILE * file, Object * parent, Object ** object, int * objectType)
206 int num = fscanf(file, "%s", buffer);
207 bool recognized = false;
209 //printf("FileIO: fscanf returned %i, buffer = \"%s\"\n", num, buffer);
211 // The following fugliness is for troubleshooting. Can remove later.
212 if ((strcmp(buffer, "END") != 0) && (strcmp(buffer, "ENDCONTAINER") != 0))
215 num = fscanf(file, " %i ", &foundLayer);
216 //printf("FileIO: fscanf returned %i, foundLayer = %i\n", num, foundLayer);
221 else if (errno == EBADF)
223 else if (errno == EILSEQ)
225 else if (errno == EINTR)
227 else if (errno == EINVAL)
229 else if (errno == ENOMEM)
231 else if (errno == ERANGE)
234 printf("errno = %i\n", errno);
238 if (strcmp(buffer, "LINE") == 0)
240 //printf(" Found LINE.\n");
243 fscanf(file, "(%lf,%lf) (%lf,%lf)", &v1.x, &v1.y, &v2.x, &v2.y);
244 //printf(" Number of params recognized: %i\n", n);
245 *object = new Line(v1, v2, parent);
246 *objectType = OTFLine;
248 else if (strcmp(buffer, "CIRCLE") == 0)
253 fscanf(file, "(%lf,%lf) %lf", &v.x, &v.y, &r);
254 *object = new Circle(v, r, parent);
255 *objectType = OTFCircle;
257 else if (strcmp(buffer, "ARC") == 0)
262 fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &v.x, &v.y, &r, &a1, &a2);
263 *object = new Arc(v, r, a1, a2, parent);
264 *objectType = OTFArc;
266 else if (strcmp(buffer, "DIMENSION") == 0)
271 fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &v1.x, &v1.y, &v2.x, &v2.y, &type);
272 *object = new Dimension(v1, v2, type, parent);
273 *objectType = OTFDimension;
275 else if (strcmp(buffer, "CONTAINER") == 0)
278 *objectType = OTFContainer;
280 else if (strcmp(buffer, "ENDCONTAINER") == 0)
283 *objectType = OTFContainerEnd;
285 else if (strcmp(buffer, "END") == 0)
288 *objectType = OTFEndOfFile;
292 (*object)->layer = foundLayer;