diff --git a/MainWindow.h b/MainWindow.h index 26204e0..f6c1a32 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -73,7 +73,6 @@ private slots: /** * Create an image paint. */ - uid createImagePaint(uid paintId, QString uri, float x, float y); // Widget callbacks. void handlePaintItemSelectionChanged(); @@ -86,6 +85,19 @@ private slots: void windowModified(); void pollOscInterface(); +public slots: + /** + * Create an image paint. + */ + uid createImagePaint(uid paintId, QString uri, float x, float y); + /** + * Creates a textured triangle. + */ + uid createTriangleTextureMapping(uid mappingId, + uid paintId, + const QList &src, const QList &dst); + + private: // Methods. void createLayout(); @@ -161,8 +173,8 @@ private: //Facade* _facade; // View. - uint currentPaintId; - uint currentMappingId; + uid currentPaintId; + uid currentMappingId; bool _hasCurrentMapping; bool _hasCurrentPaint; @@ -171,8 +183,8 @@ private: public: MappingManager& getMappingManager() { return *mappingManager; } Mapper::ptr getMapperByMappingId(uint id) { return mappers[id]; } - uint getCurrentPaintId() const { return currentPaintId; } - uint getCurrentMappingId() const { return currentMappingId; } + uid getCurrentPaintId() const { return currentPaintId; } + uid getCurrentMappingId() const { return currentMappingId; } bool hasCurrentPaint() const { return _hasCurrentPaint; } bool hasCurrentMapping() const { return _hasCurrentMapping; } void setCurrentPaint(int uid) @@ -183,7 +195,7 @@ public: void setCurrentMapping(int uid) { currentMappingId = uid; - if (uid != -1) + if (uid != NULL_UID) propertyPanel->setCurrentWidget(mappers[uid]->getPropertiesEditor()); _hasCurrentMapping = true; } diff --git a/Mapping.h b/Mapping.h index 59b75c5..efff85e 100644 --- a/Mapping.h +++ b/Mapping.h @@ -76,6 +76,8 @@ public: _shape->build(); } + virtual QString getType() const = 0; + Paint::ptr getPaint() const { return _paint; } Shape::ptr getShape() const { return _shape; } @@ -111,16 +113,23 @@ private: public: TextureMapping(Paint::ptr paint, Shape::ptr shape, - Shape::ptr inputShape) - : Mapping(paint, shape), + Shape::ptr inputShape, uid id=NULL_UID) + : Mapping(paint, shape, id), _inputShape(inputShape) - {} + { + // Only supports shape of the same type (for now). + Q_ASSERT(shape->getType() == inputShape->getType()); + } virtual void build() { Mapping::build(); _inputShape->build(); } + virtual QString getType() const { + return getShape()->getType() + "_texture"; + } + public: Shape::ptr getInputShape() const { return _inputShape; } }; diff --git a/Paint.h b/Paint.h index 5d69617..840d682 100644 --- a/Paint.h +++ b/Paint.h @@ -66,6 +66,8 @@ public: QString getName() const { return _name; } uid getId() const { return _id; } + virtual QString getType() const = 0; + private: QString _name; }; @@ -135,6 +137,8 @@ public: virtual void build() { } + virtual QString getType() const { return "image"; } + virtual int getWidth() const { return image.width(); } virtual int getHeight() const { return image.height(); } virtual const uchar* getBits() const { return image.bits(); } diff --git a/ProjectReader.cpp b/ProjectReader.cpp index a1a0db4..b3d6af8 100644 --- a/ProjectReader.cpp +++ b/ProjectReader.cpp @@ -22,22 +22,33 @@ #include #include -ProjectReader::ProjectReader(MappingManager *manager) : - _manager(manager) +ProjectReader::ProjectReader(MainWindow *window) : _window(window) { } bool ProjectReader::readFile(QIODevice *device) { - _xml.setDevice(device); - if (_xml.readNextStartElement()) - { - if (_xml.name() == "project" && _xml.attributes().value("version") == "1.0") - readProject(); - else - _xml.raiseError(QObject::tr("The file is not a libremapping version 1.0 file.")); + QString errorStr; + int errorLine; + int errorColumn; + + QDomDocument doc; + if (!doc.setContent(device, false, &errorStr, &errorLine, &errorColumn)) { + std::cerr << "Error: Parse error at line " << errorLine << ", " + << "column " << errorColumn << ": " + << qPrintable(errorStr) << std::endl; + return false; } - return ! _xml.error(); + + QDomElement root = doc.documentElement(); + if (root.tagName() != "project" || root.attribute("version") != "1.0") { + _xml.raiseError(QObject::tr("The file is not a libremapping version 1.0 file.")); + return false; + } + + parseProject(root); + + return (! _xml.hasError() ); } QString ProjectReader::errorString() const @@ -48,119 +59,223 @@ QString ProjectReader::errorString() const .arg(_xml.columnNumber()); } -void ProjectReader::readProject() -{ - // FIXME: avoid asserts - Q_ASSERT(_xml.isStartElement() && _xml.name() == "project"); - while(! _xml.atEnd() && ! _xml.hasError()) +void ProjectReader::parseProject(const QDomElement& project) +{ + QDomElement paints = project.firstChildElement("paints"); + QDomElement mappings = project.firstChildElement("mappings"); + + // Parse paints. + QDomNode paint = paints.firstChild(); + while (!paint.isNull()) { - /* Read next element.*/ - QXmlStreamReader::TokenType token = _xml.readNext(); - /* If token is just StartDocument, we'll go to next.*/ - if (token == QXmlStreamReader::StartDocument) - { - continue; - } - /* If token is StartElement, we'll see if we can read it.*/ - else if (token == QXmlStreamReader::StartElement) - { - if (_xml.name() == "paints") - continue; - else if (_xml.name() == "paint") - { - std::cout << " * paint" << std::endl; - readPaint(); - } - else if (_xml.name() == "mappings") - continue; - else if (_xml.name() == "mapping") - { - std::cout << " * mapping " << std::endl; - readMapping(); // NULL); - } - else - { - std::cout << " * skip element " << _xml.name().string() << std::endl; - //_xml.skipCurrentElement(); - } - } - } // while - _xml.clear(); -} - - -void ProjectReader::readMapping() -{ - // FIXME: we assume an Image mapping - Q_ASSERT(_xml.isStartElement() && _xml.name() == "mapping"); - const QString *paint_id_attr; - QXmlStreamAttributes attributes = _xml.attributes(); - - if (attributes.hasAttribute("", "paint_id")) - paint_id_attr = attributes.value("", "paint_id").string(); - - std::cout << " * " << "with paint ID " << paint_id_attr << std::endl; - - //QString title = _xml.readElementText(); - //item->setText(0, title); -} - -void ProjectReader::readPaint() -{ - // FIXME: we assume an Image mapping - Q_ASSERT(_xml.isStartElement() && _xml.name() == "paint"); - const QString *paint_id_attr; - const QString *uri_attr; - const QString *typeAttrValue; - QXmlStreamAttributes attributes = _xml.attributes(); - - if (attributes.hasAttribute("", "name")) - paint_id_attr = attributes.value("", "name").string(); - if (attributes.hasAttribute("", "type")) - typeAttrValue = attributes.value("", "type").string(); - - std::cout << "Found " << typeAttrValue->toStdString() << - " paint " << paint_id_attr->toStdString() << std::endl; - - /* Next element... */ - _xml.readNext(); - // /* - // * We're going to loop over the things because the order might change. - // * We'll continue the loop until we hit an EndElement named person. - // */ - while(! (_xml.tokenType() == QXmlStreamReader::EndElement && - _xml.name() == "paint")) - { - if (_xml.tokenType() == QXmlStreamReader::StartElement) - { - if (_xml.name() == "uri") - { - /* ...go to the next. */ - _xml.readNext(); - /* - * This elements needs to contain Characters so we know it's - * actually data, if it's not we'll leave. - */ - if(_xml.tokenType() != QXmlStreamReader::Characters) - { - // pass - } - //uri_attr = _xml.text().toString(); - //std::cout << "uri " << uri_attr.toStdString() << std::endl; - } - else if (_xml.name() == "width") - { - // pass - } - else if (_xml.name() == "height") - { - // pass - } - } - _xml.readNext(); + parsePaint(paint.toElement()); + paint = paint.nextSibling(); } - // TODO: call this->_manager->getController->createPaint(...) + // Parse mappings. + QDomNode mapping = mappings.firstChild(); + while (!mapping.isNull()) + { + parseMapping(mapping.toElement()); + mapping = mapping.nextSibling(); + } } +void ProjectReader::parsePaint(const QDomElement& paint) +{ + QString paintAttrId = paint.attribute("id", QString::number(NULL_UID)); + QString paintAttrName = paint.attribute("name", ""); + QString paintAttrType = paint.attribute("type", ""); + + if (paintAttrType == "image") + { + QString uri = paint.firstChildElement("uri").text(); + QString x = paint.firstChildElement("x").text(); + QString y = paint.firstChildElement("y").text(); + + uid id = _window->createImagePaint(paintAttrId.toInt(), uri, x.toFloat(), y.toFloat()); + if (id == NULL_UID) + _xml.raiseError(QObject::tr("Cannot create image with uri %1.").arg(uri)); + } + else + _xml.raiseError(QObject::tr("Unsupported paint type: %1.").arg(paintAttrType)); + +} + +void ProjectReader::parseMapping(const QDomElement& mapping) +{ + QString mappingAttrId = mapping.attribute("id", QString::number(NULL_UID)); + QString mappingAttrPaintId = mapping.attribute("paint_id", QString::number(NULL_UID)); + QString mappingAttrType = mapping.attribute("type", ""); + + // Get destination shape. + QDomElement dst = mapping.firstChildElement("destination"); + QList dstPoints; + + if (mappingAttrType == "triangle_texture") + { + // Parse destination triangle. + _parseTriangle(dst, dstPoints); + + // Get / parse source shape. + QDomElement src = mapping.firstChildElement("source"); + QList srcPoints; + _parseTriangle(src, srcPoints); + + uid id = _window->createTriangleTextureMapping(mappingAttrId.toInt(), mappingAttrPaintId.toInt(), srcPoints, dstPoints); + + if (id == NULL_UID) + _xml.raiseError(QObject::tr("Cannot create triangle texture mapping")); + } + else if (mappingAttrType == "mesh_texture") + { + + } + else + _xml.raiseError(QObject::tr("Unsupported mapping type: %1.").arg(mappingAttrType)); +} + +void ProjectReader::_parseTriangle(const QDomElement& triangle, QList& points) +{ + // Check that the element is really a triangle. + QString type = triangle.attribute("shape", ""); + if (type != "triangle") + _xml.raiseError(QObject::tr("Wrong shape type for destination: %1.").arg(type)); + + // Reset list of points. + points.clear(); + + // Add vertices. + QDomNodeList vertices = triangle.childNodes(); + if (vertices.size() != 3) + _xml.raiseError(QObject::tr("Shape has wrong number of vertices.")); + + for (int i=0; i<3; i++) + points.push_back(_parseVertex(vertices.at(i).toElement())); +} + +QPointF ProjectReader::_parseVertex(const QDomElement& vertex) +{ + return QPointF( + vertex.attribute("x", "0").toFloat(), + vertex.attribute("y", "0").toFloat() + ); +} + + +//void ProjectReader::readProject() +//{ +// // FIXME: avoid asserts +// Q_ASSERT(_xml.isStartElement() && _xml.name() == "project"); +// +// while(! _xml.atEnd() && ! _xml.hasError()) +// { +// /* Read next element.*/ +// QXmlStreamReader::TokenType token = _xml.readNext(); +// /* If token is just StartDocument, we'll go to next.*/ +// if (token == QXmlStreamReader::StartDocument) +// { +// continue; +// } +// /* If token is StartElement, we'll see if we can read it.*/ +// else if (token == QXmlStreamReader::StartElement) +// { +// if (_xml.name() == "paints") +// continue; +// else if (_xml.name() == "paint") +// { +// std::cout << " * paint" << std::endl; +// readPaint(); +// } +// else if (_xml.name() == "mappings") +// continue; +// else if (_xml.name() == "mapping") +// { +// std::cout << " * mapping " << std::endl; +// readMapping(); // NULL); +// } +// else +// { +// std::cout << " * skip element " << _xml.name().string() << std::endl; +// //_xml.skipCurrentElement(); +// } +// } +// } // while +// _xml.clear(); +//} +// +//void ProjectReader::readMapping() +//{ +// // FIXME: we assume an Image mapping +// Q_ASSERT(_xml.isStartElement() && _xml.name() == "mapping"); +// const QString *paint_id_attr; +// QXmlStreamAttributes attributes = _xml.attributes(); +// +// if (attributes.hasAttribute("", "paint_id")) +// paint_id_attr = attributes.value("", "paint_id").string(); +// +// std::cout << " * " << "with paint ID " << paint_id_attr << std::endl; +// +// //QString title = _xml.readElementText(); +// //item->setText(0, title); +//} +// +//void ProjectReader::readPaint() +//{ +// // FIXME: we assume an Image mapping +// Q_ASSERT(_xml.isStartElement() && _xml.name() == "paint"); +// const QString *paint_id_attr; +// const QString *uri_attr; +// const QString *typeAttrValue; +// QXmlStreamAttributes attributes = _xml.attributes(); +// +// if (attributes.hasAttribute("", "name")) +// paint_id_attr = attributes.value("", "name").string(); +// if (attributes.hasAttribute("", "type")) +// typeAttrValue = attributes.value("", "type").string(); +// +// std::cout << "Found " << typeAttrValue->toStdString() << +// " paint " << paint_id_attr->toStdString() << std::endl; +// +// /* Next element... */ +// _xml.readNext(); +// // /* +// // * We're going to loop over the things because the order might change. +// // * We'll continue the loop until we hit an EndElement. +// // */ +// while(! (_xml.tokenType() == QXmlStreamReader::EndElement && +// _xml.name() == "paint")) +// { +// if (_xml.tokenType() == QXmlStreamReader::StartElement) +// { +// if (_xml.name() == "uri") +// { +// /* ...go to the next. */ +// _xml.readNext(); +// /* +// * This elements needs to contain Characters so we know it's +// * actually data, if it's not we'll leave. +// */ +// if(_xml.tokenType() != QXmlStreamReader::Characters) +// { +// // pass +// } +// //uri_attr = _xml.text().toString(); +// //std::cout << "uri " << uri_attr.toStdString() << std::endl; +// } +// else if (_xml.name() == "width") +// { +// // pass +// } +// else if (_xml.name() == "height") +// { +// // pass +// } +// } +// _xml.readNext(); +// } +// +// // TODO: call this->_manager->getController->createPaint(...) +//} + diff --git a/ProjectReader.h b/ProjectReader.h index 2627830..f2dc937 100644 --- a/ProjectReader.h +++ b/ProjectReader.h @@ -19,22 +19,30 @@ */ #include -#include "MappingManager.h" +#include +#include "MainWindow.h" #include "Mapping.h" #include "Paint.h" class ProjectReader { public: - ProjectReader (MappingManager *manager); + ProjectReader (MainWindow* window); bool readFile (QIODevice *device); QString errorString() const; private: void readProject(); - void readPaint(); //Paint *item); - void readMapping(); //Mapping *item); + void parseProject(const QDomElement& project); + void parsePaint(const QDomElement& paint); + void parseMapping(const QDomElement& mapping); + + void _parseTriangle(const QDomElement& triangle, QList& points); + QPointF _parseVertex(const QDomElement& vertex); + +// void readPaint(); //Paint *item); +// void readMapping(); //Mapping *item); QXmlStreamReader _xml; - MappingManager *_manager; + MainWindow *_window; }; diff --git a/ProjectWriter.cpp b/ProjectWriter.cpp index 9be7054..7faef74 100644 --- a/ProjectWriter.cpp +++ b/ProjectWriter.cpp @@ -53,27 +53,33 @@ bool ProjectWriter::writeFile(QIODevice *device) void ProjectWriter::writeItem(Paint *item) { _xml.writeStartElement("paint"); + _xml.writeAttribute("id", QString::number(item->getId())); _xml.writeAttribute("name", item->getName()); - _xml.writeAttribute("type", "image"); + _xml.writeAttribute("type", item->getType()); - // FIXME: check paint type before casting to Image - Image *image = (Image *) item; - - _xml.writeTextElement("uri", image->getUri()); + if (item->getType() == "image") { - std::ostringstream os; - os << image->getWidth(); - _xml.writeTextElement("width", os.str().c_str()); - } + // FIXME: check paint type before casting to Image + Image *image = (Image *) item; - { - std::ostringstream os; - os << image->getHeight(); - _xml.writeTextElement("height", os.str().c_str()); - } + _xml.writeTextElement("uri", image->getUri()); + { + std::ostringstream os; + os << image->getX(); + _xml.writeTextElement("x", os.str().c_str()); + } - _xml.writeEndElement(); - //_xml.writeEmptyElement("hello"); + { + std::ostringstream os; + os << image->getY(); + _xml.writeTextElement("y", os.str().c_str()); + } + + _xml.writeEndElement(); + //_xml.writeEmptyElement("hello"); + } + else + qDebug() << "Unknown type, cannot save: " << item->getType() << endl; } void ProjectWriter::writeShapeVertices(Shape *shape) @@ -82,16 +88,8 @@ void ProjectWriter::writeShapeVertices(Shape *shape) { Point *point = shape->getVertex(i); _xml.writeStartElement("vertex"); - { - std::ostringstream os; - os << point->x(); - _xml.writeAttribute("x", os.str().c_str()); - } - { - std::ostringstream os; - os << point->y(); - _xml.writeAttribute("y", os.str().c_str()); - } + _xml.writeAttribute("x", QString::number(point->x())); + _xml.writeAttribute("y", QString::number(point->y())); _xml.writeEndElement(); // vertex } } @@ -99,11 +97,14 @@ void ProjectWriter::writeShapeVertices(Shape *shape) void ProjectWriter::writeItem(Mapping *item) { _xml.writeStartElement("mapping"); - _xml.writeAttribute("paint_id", item->getPaint()->getName()); + qDebug() << "ID: " << item->getId() << endl; + _xml.writeAttribute("id", QString::number(item->getId())); + _xml.writeAttribute("paint_id", QString::number(item->getPaint()->getId())); + _xml.writeAttribute("type", item->getType()); Shape *shape = item->getShape().get(); _xml.writeStartElement("destination"); - _xml.writeAttribute("shape", shape->getShapeType()); + _xml.writeAttribute("shape", shape->getType()); writeShapeVertices(shape); _xml.writeEndElement(); // shape @@ -111,7 +112,7 @@ void ProjectWriter::writeItem(Mapping *item) TextureMapping *tex = (TextureMapping *) item; shape = tex->getInputShape().get(); _xml.writeStartElement("source"); - _xml.writeAttribute("shape", shape->getShapeType()); + _xml.writeAttribute("shape", shape->getType()); writeShapeVertices(shape); _xml.writeEndElement(); // shape diff --git a/Shape.h b/Shape.h index d9644a5..d1518b1 100644 --- a/Shape.h +++ b/Shape.h @@ -104,7 +104,7 @@ public: vertices[i]->setY(y); } - virtual const char * getShapeType() = 0; + virtual QString getType() const = 0; /** Return true if Shape includes point (x,y), false otherwise * Algorithm should work for all polygons, including non-convex @@ -148,7 +148,7 @@ public: } virtual ~Quad() {} - virtual const char * getShapeType() { return "quad"; } + virtual QString getType() const { return "quad"; } }; /** @@ -165,7 +165,7 @@ public: _addVertex(p3); } virtual ~Triangle() {} - virtual const char * getShapeType() { return "triangle"; } + virtual QString getType() const { return "triangle"; } }; class Mesh : public Quad { @@ -176,6 +176,8 @@ public: Mesh(QPointF p1, QPointF p2, QPointF p3, QPointF p4, int nColumns=2, int nRows=2); virtual ~Mesh() {} + virtual QString getType() const { return "mesh"; } + void resizeVertices2d(std::vector< std::vector >& vertices2d, int nColumns, int nRows); void init(int nColumns, int nRows);