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