X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Ffileio.cpp;h=ad303f469cdcb0713a9afcb4e67533c1b428c3f3;hb=3c890e51a9763ffcee49e15753453a7da248272b;hp=7cd6816d4766f63690d14d7ce0dbca13c7bc31b3;hpb=baf67656b97e3d61e9223e66ebe4f554e364cd4a;p=architektonas diff --git a/src/fileio.cpp b/src/fileio.cpp index 7cd6816..ad303f4 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -8,32 +8,532 @@ // JLH = James Hammons // // Who When What -// --- ---------- ------------------------------------------------------------- +// --- ---------- ------------------------------------------------------------ // JLH 02/20/2013 Created this file // #include "fileio.h" //#include +#include #include -#include "container.h" +#include +#include +#include "applicationwindow.h" +#include "drawingview.h" +#include "global.h" +#include "structs.h" +/* +How to handle connected objects +------------------------------- -/*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * object) +Every Object has a vector which enumerates all Objects connected to +the one we're looking at. So it looks like we'll have to take a two pass +approach to loading and saving. + +Basically, in the saving case, first we write out all objects, keeping a +pointer-to-index-number record. Second, we loop through all the objects we +wrote out, writing connection lists. Format (indices are 1-based): + +CONNECTIONS +1: 12 3 +3: 1 +12: 1 +ENDCONNECTIONS + +In the reading case, we do pretty much the same: we construct pointer-to-index- +number list, then read the connection list. The PTIN connects the index to the +Object pointers we've created. Then we simply call the Object's Connect() +function to connect the objects. + +Small problem though: How does a Dimension know which points on a Line it's +connected to? This approach tells the Dimension it's connected to the Line and +the Line that it's connected to the Dimension, but not which points. + +How to handle them then? Do we list the point with the Object pointed at? A +Line can contain an infinite number of points to connect with besides its +endpoints. + +So with each connection Object in the vector, there would also have to be a +corresponding point to go with it, that would be gotten from the other Object's +Connect() function. Or, instead of a point, a parameter value? + +Doing that, with the Line and a parameter "t", if t == 0 we have endpoint 1. +if t == 1, then we have endpoint 2. With a Circle, the parameter is a number +between 0 and 1 (scaled to 0 to tau). With an Arc, the parameter goes from 0 to +1, 0 being enpoint 1 and 1 being endpoint 2. + +How does this work for moving objects that are connected? Again, with the Line +and Dimension. The Line's connections looks like this: + +Object *: dim1, t = 0 +Object *: dim1, t = 1 + +Dimension looks like this: + +Object *: line1, t = 0 +Object *: line1, t = 1 + +For Dimensions, it can query the connected object (if any) using something like +GetPointForParameter(). That way it can figure out where its endpoints are. If +there is no connected point, then it uses its internal point. + + +Dimensions are special cases of lines: They have exactly *two* points and none +in between. Therefore, the Dimension object only needs to have two points. But +those points can be connected to multiple objects. The can also be connected to +no points/Objects too. + +How to describe them and their connections (or lack thereof)? + +Would have to be a 2nd pass, after all objects have been written out in order. +Then you could do something like: + +DIMCONNECTIONS +8 (the Dimension #): 1 (the Object # for point 1) 1 (the Object # for point 2) +ENDDIMCONNECTIONS + + + +Connection attributes: E.g., between a Line a Circle, it can be tangent, +perpendicular, or an arbitrary angle. How to encode that information? It's not +intrinsic to either the Line or the Circle, but is a function of the +relationship between them by virtue of their connection. + + +OTHER CONSIDERATIONS: +--------------------- + + - Need to figure out how to store the Layer list (should layer list be optional?) + - Need to figure out how to store the Block list and blocks + +*/ + +enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFObject, OTFEndOfFile }; + +// Instantiate class variables +/*static*/ int FileIO::objectFileType = OTFObject; + +/*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * c) +{ + /* Approach: loop through the container, doing a depth-first traversal. Any + extra containers found are looped through until there aren't any more + down, then ordinary objects are described. This can be handled by a + virtual Object function that reports the object by itself if it's a non- + Container, otherwise it enumerates all objects within itself. */ + + fprintf(file, "ARCHITEKTONAS DRAWING V1.2\n"); + fprintf(file, "PROPERTIES 4\n"); + fprintf(file, "BASE_UNIT %i\n", c->baseUnit); + fprintf(file, "UNIT_STYLE %i\n", c->unitStyle); + fprintf(file, "DEC_PREC %i\n", c->decimalPrecision); + fprintf(file, "FRAC_PREC %i\n", c->fractionalPrecision); + + fprintf(file, "LAYERS %i\n", Global::numLayers); + + for(int i=0; i containerStack; + Container * currentTopContainer = drawing; + ResetLayerVectors(); + + while (!feof(file)) + { + // Reconstruct the object + Object * obj = GetObjectFromFile(file); + + // objectFileType is set in GetObjectFromFile()... + if (objectFileType == OTFObject) + { + if (obj == NULL) + return false; + + currentTopContainer->objects.push_back(obj); +//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size()); + + // If the object is a container, push current TLC on the stack and + // set it as the new TLC + if (obj->type == OTContainer) + { + containerStack.push_back(currentTopContainer); + currentTopContainer = (Container *)obj; + } + } + else if (objectFileType == OTFContainerEnd) + { + // Container is done, so pop the stack to get back the previous TLC + currentTopContainer = containerStack.back(); + containerStack.pop_back(); + } + else if (objectFileType == OTFEndOfFile) + { +//printf("Load: container size = %li\n", drawing->objects.size()); + return true; + } + } return false; } +/*static*/ bool FileIO::LoadVersion1_1(FILE * file, Container * drawing) +{ + // Approach: read each object in the file, one by one. If the object is a + // Container, add objects to it until an "endContainer" marker is found. + // This will require a stack to maintain the current Container. + std::vector containerStack; + Container * currentTopContainer = drawing; + ResetLayerVectors(); + + while (!feof(file)) + { + // Reconstruct the object (extended format!) + Object * obj = GetObjectFromFile(file, true); + + // objectFileType is set in GetObjectFromFile()... + if (objectFileType == OTFObject) + { + if (obj == NULL) + return false; + + currentTopContainer->objects.push_back(obj); +//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size()); -/*static*/ bool FileIO::LoadAtnsFile(FILE * file, Container * object) + // If the object is a container, push current TLC on the stack and + // set it as the new TLC + if (obj->type == OTContainer) + { + containerStack.push_back(currentTopContainer); + currentTopContainer = (Container *)obj; + } + } + else if (objectFileType == OTFContainerEnd) + { + // Add the extents of the current container + Rect r = ApplicationWindow::drawing->GetObjectExtents((Object *)currentTopContainer); + currentTopContainer->p[0] = r.TopLeft(); + currentTopContainer->p[1] = r.BottomRight(); +//printf("Container extents: <%lf, %lf>, <%lf, %lf>\n", r.l, r.t, r.r, r.b); + + // Container is done, so pop the stack to get back the previous TLC + currentTopContainer = containerStack.back(); + containerStack.pop_back(); + } + else if (objectFileType == OTFEndOfFile) + { +//printf("Load: container size = %li\n", drawing->objects.size()); + return true; + } + } + + return false; +} + +/*static*/ bool FileIO::LoadVersion1_2(FILE * file, Container * drawing) { -// QString filename2 = QFileDialog::getOpenFileName(NULL, tr("Open Drawing"), -// "", tr("Architektonas files (*.drawing)")); + int hidden, locked, props = 0; + char textBuffer[65536]; + + // Load drawing properties, if any, first + fscanf(file, "PROPERTIES %i\n", &props); + + if (props == 4) + { + fscanf(file, "BASE_UNIT %i\n", &(drawing->baseUnit)); + fscanf(file, "UNIT_STYLE %i\n", &(drawing->unitStyle)); + fscanf(file, "DEC_PREC %i\n", &(drawing->decimalPrecision)); + fscanf(file, "FRAC_PREC %i\n", &(drawing->fractionalPrecision)); + } + + // Load layer information next + fscanf(file, "LAYERS %i\n", &Global::numLayers); + + for(int i=0; i containerStack; + Container * currentTopContainer = drawing; + + while (!feof(file)) + { + // Reconstruct the object (extended format!) + Object * obj = GetObjectFromFile(file, true, true); + + // objectFileType is set in GetObjectFromFile()... + if (objectFileType == OTFObject) + { + if (obj == NULL) + return false; + + currentTopContainer->objects.push_back(obj); +//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size()); + + // If the object is a container, push current TLC on the stack and + // set it as the new TLC + if (obj->type == OTContainer) + { + containerStack.push_back(currentTopContainer); + currentTopContainer = (Container *)obj; + } + } + else if (objectFileType == OTFContainerEnd) + { + // Add the extents of the current container + Rect r = ApplicationWindow::drawing->GetObjectExtents((Object *)currentTopContainer); + currentTopContainer->p[0] = r.TopLeft(); + currentTopContainer->p[1] = r.BottomRight(); +//printf("Container extents: <%lf, %lf>, <%lf, %lf>\n", r.l, r.t, r.r, r.b); + + // Container is done, so pop the stack to get back the previous TLC + currentTopContainer = containerStack.back(); + containerStack.pop_back(); + } + else if (objectFileType == OTFEndOfFile) + { +//printf("Load: container size = %li\n", drawing->objects.size()); + return true; + } + } return false; } +/*static*/ Object * FileIO::GetObjectFromFile(FILE * file, bool extended/*= false*/, bool ext2/*= false*/) +{ + char buffer[256]; + char textBuffer[65536]; + int foundLayer = 0; + /*int num =*/ fscanf(file, "%s", buffer); + Object * obj = NULL; + objectFileType = OTFObject; +//printf("FileIO: fscanf returned %i, buffer = \"%s\"\n", num, buffer); +// The following fugliness is for troubleshooting. Can remove later. + if ((strcmp(buffer, "END") != 0) && (strcmp(buffer, "ENDCONTAINER") != 0)) +{ +errno = 0; + /*num =*/ fscanf(file, " %i ", &foundLayer); +//printf("FileIO: fscanf returned %i, foundLayer = %i\n", num, foundLayer); +if (errno) +{ + if (errno == EAGAIN) + printf("EAGAIN\n"); + else if (errno == EBADF) + printf("EBADF\n"); + else if (errno == EILSEQ) + printf("EILSEQ\n"); + else if (errno == EINTR) + printf("EINTR\n"); + else if (errno == EINVAL) + printf("EINVAL\n"); + else if (errno == ENOMEM) + printf("ENOMEM\n"); + else if (errno == ERANGE) + printf("ERANGE\n"); + else + printf("errno = %i\n", errno); +} +} + + // Need to add pen attributes as well... do that for v1.1 :-P + // And fill attributes... do that for v1.3 (1.2 added layers) + if (strcmp(buffer, "LINE") == 0) + { + Point p1, p2; + fscanf(file, "(%lf,%lf) (%lf,%lf)", &p1.x, &p1.y, &p2.x, &p2.y); + obj = (Object *)new Line(p1, p2); + } + else if (strcmp(buffer, "CIRCLE") == 0) + { + Point p; + double r; + fscanf(file, "(%lf,%lf) %lf", &p.x, &p.y, &r); + obj = (Object *)new Circle(p, r); + } + else if (strcmp(buffer, "ARC") == 0) + { + Point p; + double r, a1, a2; + fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &p.x, &p.y, &r, &a1, &a2); + obj = (Object *)new Arc(p, r, a1, a2); + } + else if (strcmp(buffer, "POLYLINE") == 0) + { + long int size; + obj = (Object *)new Polyline(); + fscanf(file, "(%li)", &size); + fscanf(file, " (%i, %f, %i)\n", &obj->color, &obj->thickness, &obj->style); + + for(int i=0; ip[0].x, &po->p[0].y, &po->length); + ((Polyline *)obj)->Add(po); + } + } + else if (strcmp(buffer, "TEXT") == 0) + { + Point p; + fscanf(file, "(%lf,%lf) \"%[^\"]\"", &p.x, &p.y, textBuffer); + obj = (Object *)new Text(p, textBuffer); + } + else if (strcmp(buffer, "DIMENSION") == 0) + { + Point p1, p2; + DimensionType type; + double offset = 0; + fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &p1.x, &p1.y, &p2.x, &p2.y, (int *)&type); + + if (ext2) + fscanf(file, " %lf", &offset); + + obj = (Object *)new Dimension(p1, p2, type, offset); + } + else if (strcmp(buffer, "CONTAINER") == 0) + { + obj = (Object *)new Container(); +// objectFileType = OTFContainer; + } + else if (strcmp(buffer, "ENDCONTAINER") == 0) + { + objectFileType = OTFContainerEnd; + } + else if (strcmp(buffer, "END") == 0) + { + objectFileType = OTFEndOfFile; + } + else + printf("Unknown object type '%s'...\n", buffer); + + if (obj != NULL) + { + obj->layer = foundLayer; + + if (extended && (obj->type != OTContainer) && (obj->type != OTPolyline)) + { + fscanf(file, " (%i, %f, %i)\n", &obj->color, &obj->thickness, &obj->style); + } + } + + return obj; +} + +/*static*/ bool FileIO::WriteObjectToFile(FILE * file, Object * obj) +{ + // Sanity check + if (obj == NULL) + return false; + + switch (obj->type) + { + case OTLine: + fprintf(file, "LINE %i (%.16lf,%.16lf) (%.16lf,%.16lf)", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y); + break; + case OTCircle: + fprintf(file, "CIRCLE %i (%.16lf,%.16lf) %.16lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0]); + break; + case OTEllipse: + break; + case OTArc: + fprintf(file, "ARC %i (%.16lf,%.16lf) %.16lf, %.16lf, %.16lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0], obj->angle[0], obj->angle[1]); + break; + case OTPolyline: + { + Polyline * p = (Polyline *)obj; + fprintf(file, "POLYLINE %i (%li)", p->layer, p->points.size()); + fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style); + + for(VPVectorIter i=p->points.begin(); i!=p->points.end(); i++) + { + Object * po = (Object *)(*i); + fprintf(file, "(%.16lf,%.16lf,%.16lf)\n", po->p[0].x, po->p[0].y, po->length); + } + } + break; + case OTDimension: + fprintf(file, "DIMENSION %i (%.16lf,%.16lf) (%.16lf,%.16lf) %i %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y, ((Dimension *)obj)->subtype, ((Dimension *)obj)->offset); + break; + case OTSpline: + break; + case OTText: + fprintf(file, "TEXT %i (%lf,%lf) \"%s\"", obj->layer, obj->p[0].x, obj->p[0].y, ((Text *)obj)->s.c_str()); + break; + case OTContainer: + { + Container * c = (Container *)obj; + + if (c->topLevel == false) + fprintf(file, "CONTAINER %i\n", obj->layer); + + std::vector::iterator i; + + for(i=c->objects.begin(); i!=c->objects.end(); i++) + WriteObjectToFile(file, (Object *)*i); + + if (c->topLevel == false) + fprintf(file, "ENDCONTAINER\n"); + + break; + } + default: + break; + } + + if ((obj->type != OTContainer) && (obj->type != OTPolyline)) + fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style); + + return true; +}