From a420513cd543843913dff31e867c432386938ad5 Mon Sep 17 00:00:00 2001 From: Pedro Monteiro Date: Fri, 17 May 2024 23:43:38 +0100 Subject: [PATCH] Final commit, end of project --- README.md | 6 ++- SVGElements.cpp | 135 +++++++++++++++++++++++++++++++++++++++--------- SVGElements.hpp | 24 ++++++--- readSVG.cpp | 86 ++++++++++++------------------ 4 files changed, 166 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 86176c3..fd46421 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,8 @@ All types of transformations (translate, rotate and scale) are well implemented ### Groups -### Element duplication \ No newline at end of file +The groups have been implemented using a derived subclass from SVGElement and uses a recursive function in [readSVG.cpp](readSVG.cpp) with all transformations working. + +### Element duplication + +For elements that have an "id" attribute, they can be easily duplicated using and their respective transformations. \ No newline at end of file diff --git a/SVGElements.cpp b/SVGElements.cpp index 164fceb..043c36a 100644 --- a/SVGElements.cpp +++ b/SVGElements.cpp @@ -30,16 +30,33 @@ namespace svg img.draw_ellipse(center, radius, fill); } + /** + * @brief Translates the center of the ellipse by the given translation vector. + * + * @param t The translation vector. + */ void Ellipse::translate(const Point &t) { center = center.translate(t); } + /** + * @brief Rotates the ellipse around a specified origin by a given number of degrees. + * + * @param origin The origin point around which the ellipse will be rotated. + * @param degrees The number of degrees by which the ellipse will be rotated. + */ void Ellipse::rotate(const Point &origin, int degrees) { center = center.rotate(origin, degrees); } + /** + * @brief Scales the ellipse by a given factor around a specified origin point. + * + * @param origin The origin point around which the ellipse will be scaled. + * @param v The scaling factor. + */ void Ellipse::scale(const Point &origin, int v) { center = center.scale(origin, v); @@ -66,23 +83,6 @@ namespace svg img.draw_ellipse(center, radius, fill); } - /* void Circle::translate(const Point &t) - { - center = center.translate(t); - } - - void Circle::rotate(const Point &origin, int degrees) - { - center = center.rotate(origin, degrees); - } - - void Circle::scale(const Point &origin, int v) - { - center = center.scale(origin, v); - radius.x = radius.x * v; - radius.y = radius.y * v; - } */ - /** * @brief Creates a copy of the Circle object. * @@ -117,6 +117,11 @@ namespace svg } } + /** + * @brief Translates the points of the polyline by the given translation vector. + * + * @param t The translation vector. + */ void Polyline::translate(const Point &t) { for (size_t i = 0; i < points.size(); i++) { @@ -124,6 +129,12 @@ namespace svg } } + /** + * @brief Rotates the polyline around a specified origin by a given number of degrees. + * + * @param origin The origin point around which the polyline will be rotated. + * @param degrees The number of degrees by which the polyline will be rotated. + */ void Polyline::rotate(const Point &origin, int degrees) { for (size_t i = 0; i < points.size(); i++) @@ -132,6 +143,12 @@ namespace svg } } + /** + * @brief Scales the polyline by a given factor around a specified origin point. + * + * @param origin The origin point around which the polyline will be scaled. + * @param v The scaling factor. + */ void Polyline::scale(const Point &origin, int v) { for (size_t i = 0; i < points.size(); i++) @@ -159,18 +176,35 @@ namespace svg img.draw_line(start, end, stroke); } + /** + * @brief Translates the points of the line by the given translation vector. + * + * @param t The translation vector. + */ void Line::translate(const Point &t) { start = start.translate(t); end = end.translate(t); } + /** + * @brief Rotates the line around a specified origin by a given number of degrees. + * + * @param origin The origin point around which the line will be rotated. + * @param degrees The number of degrees by which the line will be rotated. + */ void Line::rotate(const Point &origin, int degrees) { start = start.rotate(origin, degrees); end = end.rotate(origin, degrees); } + /** + * @brief Scales the line by a given factor around a specified origin point. + * + * @param origin The origin point around which the line will be scaled. + * @param v The scaling factor. + */ void Line::scale(const Point &origin, int v) { start = start.scale(origin, v); @@ -209,6 +243,11 @@ namespace svg img.draw_polygon(points, fill); } + /** + * @brief Translates the points of the polygon by the given translation vector. + * + * @param t The translation vector. + */ void Polygon::translate(const Point &t) { for (size_t i = 0; i < points.size(); i++) @@ -217,6 +256,12 @@ namespace svg } } + /** + * @brief Rotates the polygon around a specified origin by a given number of degrees. + * + * @param origin The origin point around which the polygon will be rotated. + * @param degrees The number of degrees by which the polygon will be rotated. + */ void Polygon::rotate(const Point &origin, int degrees) { for (size_t i = 0; i < points.size(); i++) @@ -225,6 +270,12 @@ namespace svg } } + /** + * @brief Scales the polygon by a given factor around a specified origin point. + * + * @param origin The origin point around which the polygon will be scaled. + * @param v The scaling factor. + */ void Polygon::scale(const Point &origin, int v) { for (size_t i = 0; i < points.size(); i++) @@ -261,36 +312,63 @@ namespace svg return new Rect(points, fill, id); } - - + /** + * @brief Draws a group on the given PNGImage. + * + * @param img The PNGImage to draw on. + */ void Group::draw(PNGImage &img) const { for (auto y : V ){ - y.draw(img); + y->draw(img); } } + /** + * @brief Translates the elements of the group by the given translation vector. + * + * @param t The translation vector. + */ void Group::translate(const Point &t) { for (auto y : V ){ - y.translate(t); + y->translate(t); } } + /** + * @brief Rotates the elements of the group around a specified origin by a given number of degrees. + * + * @param origin The origin point around which the elements will be rotated. + * @param degrees The number of degrees by which the elements will be rotated. + */ void Group::rotate(const Point &origin, int degrees) { for (auto y : V ){ - y.rotate(origin,degrees); + y->rotate(origin,degrees); } } + /** + * @brief Scales the elements of the group by a given factor around a specified origin point. + * + * @param origin The origin point around which the elements will be scaled. + * @param v The scaling factor. + */ void Group::scale(const Point &origin, int v) { for (auto y : V ){ - y.scale(origin,v); + y->scale(origin,v); } } + + /** + * @brief Destructor for the Group class. + * + * This destructor is responsible for freeing the memory allocated for the objects + * stored in the `V` vector. + */ Group::~Group() { for (auto y : V ){ @@ -298,7 +376,16 @@ namespace svg } } + /** + * @brief Creates a deep copy of the Group object. + * + * @return A pointer to the newly created Group object. + */ SVGElement* Group::copy() const{ - return new Group(VectorFigs); + std::vector temp; + for (auto y : V ){ + temp.push_back(y->copy()); + } + return new Group(temp); } } diff --git a/SVGElements.hpp b/SVGElements.hpp index 48eb86e..3fc1831 100644 --- a/SVGElements.hpp +++ b/SVGElements.hpp @@ -209,18 +209,28 @@ namespace svg SVGElement* copy() const override; // Declaration of the Rectangle's copy function. }; + /** + * @class Group + * @brief Represents a group of SVG elements. + */ class Group:public SVGElement { public: - Group(const std::vector &VectorFigs) : V(VectorFigs) {} - void draw(PNGImage &img) const override; - void translate(const Point &t) override; - void rotate(const Point &origin, - int degrees) override; + /** + * @brief Constructs a Group object with the given vector of SVGElement pointers. + * + * @param VectorFigs The vector of SVGElement pointers to be stored in the Group. + */ + Group(const std::vector &VectorFigs) + : V(VectorFigs) {} + void draw(PNGImage &img) const override; // Declaration of the Groups's draw function. + void translate(const Point &t) override; // Declaration of the Groups's translate function. + void rotate(const Point &origin, + int degrees) override; // Declaration of the Groups's rotate function. void scale(const Point &origin, - int v) override; + int v) override; // Declaration of the Groups's scale function. ~Group(); - SVGElement* copy() const override; + SVGElement* copy() const override; // Declaration of the Groups's copy function. private: std::vector V; }; diff --git a/readSVG.cpp b/readSVG.cpp index b871643..972a740 100644 --- a/readSVG.cpp +++ b/readSVG.cpp @@ -11,25 +11,7 @@ using namespace tinyxml2; namespace svg { - void identificadorload(vector &pFigs, map> &identif, const string &ref) - { - vector temp; - string ident = ref.substr(1, string::npos); // tirar o '#' do href(referencia)// - temp = identif[ident]; - for (SVGElement *elem : temp) - { - pFigs.push_back(elem->copy()); - } - } - void identificadorsave(const vector &pFigs, map> &identif, const string &ident) - { - vector temp; - for (SVGElement *elem : pFigs) - { - temp.push_back(elem->copy()); - } - identif[ident] = temp; - } + map mapa_use; /** * @brief Transforms all the commas in a string to spaces. @@ -142,21 +124,25 @@ namespace svg } } - SVGElement* recursive(XMLElement *pParent) + /** + * @brief Recursively parses an XML element and creates corresponding SVG elements. + * @param pParent The parent XML element to parse. + * @return A pointer to the created SVG element. + */ + SVGElement *recursive(XMLElement *pParent) { vector figsofgrupos; for (XMLElement *child = pParent->FirstChildElement(); child != nullptr; child = child->NextSiblingElement()) { - SVGElement * p; + SVGElement *p; + // Check which element to add to figsofgrupos. if (strcmp(child->Name(), "ellipse") == 0) { p = new Ellipse(parse_color(child->Attribute("fill")), {child->IntAttribute("cx"), child->IntAttribute("cy")}, {child->IntAttribute("rx"), child->IntAttribute("ry")}); - } else if (strcmp(child->Name(), "circle") == 0) { p = new Circle(parse_color(child->Attribute("fill")), {child->IntAttribute("cx"), child->IntAttribute("cy")}, child->IntAttribute("r")); - } else if (strcmp(child->Name(), "polyline") == 0) { @@ -165,18 +151,17 @@ namespace svg istringstream iss(pontos); vector polypontos; Point temp; + // Get coordinates (x, y) from input. while (iss >> temp.x) { iss >> temp.y; polypontos.push_back(temp); } p = new Polyline(polypontos, parse_color(child->Attribute("stroke"))); - } else if (strcmp(child->Name(), "line") == 0) { - p = new Line({child->IntAttribute("x1"), child->IntAttribute("y1")}, {child->IntAttribute("x2"), child->IntAttribute("y2")}, parse_color(child->Attribute("stroke"))); - + p = new Line({child->IntAttribute("x1"), child->IntAttribute("y1")}, {child->IntAttribute("x2"), child->IntAttribute("y2")}, parse_color(child->Attribute("stroke"))); } else if (strcmp(child->Name(), "polygon") == 0) { @@ -185,13 +170,13 @@ namespace svg istringstream iss(pontos); vector polypontos; Point temp; + // Get coordinates (x, y) from input. while (iss >> temp.x) { iss >> temp.y; polypontos.push_back(temp); } p = new Polygon(polypontos, parse_color(child->Attribute("fill"))); - } else if (strcmp(child->Name(), "rect") == 0) { @@ -212,56 +197,51 @@ namespace svg corner4.x = x; corner4.y = y + height - 1; - vector points; + vector points; // Add every corner of the rectangle to a vector of points. points.push_back(corner1); points.push_back(corner2); points.push_back(corner3); points.push_back(corner4); - p = new Rect(points, parse_color(child->Attribute("fill"))); - + p = new Rect(points, parse_color(child->Attribute("fill"))); } else if (strcmp(child->Name(), "g") == 0) { - SVGElement* p=recursive(child); - - } + p = recursive(child); // Recursive case call for groups. } else if (strcmp(child->Name(), "use") == 0) { string ref = child->Attribute("href"); - identificadorload(figsofgrupos, identif, ref); + string ident = ref.substr(1, string::npos); + // Copy the object from the map using the identifier as the key + p = mapa_use[ident]->copy(); } + // Initialize an empty identifier string ident = ""; if (child->Attribute("id")) { + // Get the id attribute of the child ident = child->Attribute("id"); - identificadorsave(figsofgrupos, identif, ident); + // Add the object to the map with the identifier as the key + mapa_use[ident] = p; } - if (child->Attribute("transform")){ + if (child->Attribute("transform")) + { const char *transform_attr = child->Attribute("transform"); const char *transform_origin = child->Attribute("transform-origin"); parseTransform(p, transform_attr, transform_origin); } figsofgrupos.push_back(p); - - return new Group(figsofgrupos); - } - - - void delmapa(map> &identif) - { - // Iterate through the map and delete each vector of SVGElement pointers - for (auto &entry : identif) - { - for (SVGElement *element : entry.second) - { - delete element; // Deallocate memory for each SVGElement - } - entry.second.clear(); // Clear the vector } - identif.clear(); // Clear the entire map + return new Group(figsofgrupos); } + /** + * Reads an SVG file and extracts the dimensions and SVG elements. + * + * @param svg_file The path to the SVG file to be read. + * @param dimensions The reference to a Point object where the dimensions of the SVG will be stored. + * @param svg_elements The reference to a vector of SVGElement pointers where the extracted SVG elements will be stored. + */ void readSVG(const string &svg_file, Point &dimensions, vector &svg_elements) { XMLDocument doc; @@ -274,7 +254,7 @@ namespace svg dimensions.x = xml_elem->IntAttribute("width"); dimensions.y = xml_elem->IntAttribute("height"); - SVGElement* A = recursive(xml_elem, svg_elements, identif); + SVGElement *A = recursive(xml_elem); svg_elements.push_back(A); } } \ No newline at end of file