diff --git a/cyberiadamlpp.cpp b/cyberiadamlpp.cpp index 0c5309d..e442d87 100644 --- a/cyberiadamlpp.cpp +++ b/cyberiadamlpp.cpp @@ -36,10 +36,10 @@ namespace Cyberiada { static const String DEFAULT_GRAPHML_FORMAT = "Cyberiada-GraphML-1.0"; static const String META_NODE_NAME = "CGML_META"; static const String META_NODE_ID = "nMeta"; - const String VERTEX_ID_PREFIX = "n"; - const String SM_ID_PREFIX = "G"; - const String TRANTISION_ID_SEP = "-"; - const String TRANTISION_ID_NUM_SEP = "#"; + static const String VERTEX_ID_PREFIX = "n"; + static const String SM_ID_PREFIX = "G"; + static const String TRANTISION_ID_SEP = "-"; + static const String TRANTISION_ID_NUM_SEP = "#"; static const std::string tab = "\t"; }; @@ -286,6 +286,50 @@ std::ostream& Action::dump(std::ostream& os) const // Geometry objects // ----------------------------------------------------------------------------- +bool Rect::operator==(const Rect& r) +{ + if (!valid && !r.valid) return true; + if (!valid || !r.valid) return false; + return x == r.x && y == r.y && width == r.width && height == r.height; +} + +void Rect::expand(const Point& p) +{ + if (p.valid) { + if (valid) { + if (p.x < x) x = p.x; + if (p.x > x + width) width = p.x - x; + if (p.y < y) y = p.y; + if (p.y > y + height) height = p.y - y; + } else { + x = p.x; + y = p.y; + width = height = 0.0; + } + } +} + +void Rect::expand(const Rect& r) +{ + if (r.valid) { + if (valid) { + expand(Point(r.x, r.y)); + expand(Point(r.x + r.width, r.y + r.height)); + } else { + *this = r; + } + } +} + +void Rect::expand(const Polyline& pl) +{ + if (pl.size() > 0) { + for (Polyline::const_iterator i = pl.begin(); i != pl.end(); i++) { + expand(*i); + } + } +} + std::ostream& Cyberiada::operator<<(std::ostream& os, const Point& p) { if (!p.valid) { @@ -348,6 +392,23 @@ CommentSubject& CommentSubject::operator=(const CommentSubject& cs) return *this; } +Rect CommentSubject::get_bound_rect() const +{ + Rect r; + if (has_geometry()) { + if (has_geometry_source_point()) { + r.expand(source_point); + } + if (has_geometry_target_point()) { + r.expand(target_point); + } + if (has_polyline()) { + r.expand(polyline); + } + } + return r; +} + std::ostream& Cyberiada::operator<<(std::ostream& os, const CommentSubject& cs) { cs.dump(os); @@ -499,6 +560,20 @@ CyberiadaEdge* Comment::subjects_to_edges() const return result; } +Rect Comment::get_bound_rect() const +{ + Rect r; + if (has_geometry()) { + r = geometry_rect; + } + if (has_subjects()) { + for (std::list::const_iterator i = subjects.begin(); i != subjects.end(); i++) { + r.expand(i->get_bound_rect()); + } + } + return r; +} + std::ostream& Comment::dump(std::ostream& os) const { Element::dump(os); @@ -534,6 +609,15 @@ Vertex::Vertex(Element* _parent, ElementType _type, const ID& _id, const Name& _ { } +Rect Vertex::get_bound_rect() const +{ + Rect r; + if (has_geometry()) { + r.expand(geometry_point); + } + return r; +} + std::ostream& Vertex::dump(std::ostream& os) const { Element::dump(os); @@ -876,6 +960,15 @@ CyberiadaNode* ElementCollection::to_node() const return node; } +Rect ElementCollection::get_bound_rect() const +{ + Rect r; + if (has_geometry()) { + r = geometry_rect; + } + return r; +} + std::ostream& ElementCollection::dump(std::ostream& os) const { if (has_geometry()) { @@ -967,6 +1060,15 @@ CyberiadaNode* ChoicePseudostate::to_node() const return node; } +Rect ChoicePseudostate::get_bound_rect() const +{ + Rect r; + if (has_geometry()) { + r = geometry_rect; + } + return r; +} + std::ostream& ChoicePseudostate::dump(std::ostream& os) const { Element::dump(os); @@ -1160,6 +1262,26 @@ CyberiadaEdge* Transition::to_edge() const return edge; } +Rect Transition::get_bound_rect() const +{ + Rect r; + if (has_geometry()) { + if (has_geometry_source_point()) { + r.expand(source_point); + } + if (has_geometry_target_point()) { + r.expand(target_point); + } + if (has_geometry_label_point()) { + r.expand(label_point); + } + if (has_polyline()) { + r.expand(polyline); + } + } + return r; +} + std::ostream& Transition::dump(std::ostream& os) const { Element::dump(os); @@ -1243,6 +1365,19 @@ std::list StateMachine::get_transitions() return result; } +Rect StateMachine::get_bound_rect() const +{ + Rect r; + if (has_geometry()) { + r = ElementCollection::get_bound_rect(); + } else if (has_children()) { + for (ElementList::const_iterator i = children.begin(); i != children.end(); i++) { + r.expand((*i)->get_bound_rect()); + } + } + return r; +} + std::ostream& StateMachine::dump(std::ostream& os) const { Element::dump(os); diff --git a/cyberiadamlpp.h b/cyberiadamlpp.h index 559fc4e..106f78f 100644 --- a/cyberiadamlpp.h +++ b/cyberiadamlpp.h @@ -61,6 +61,47 @@ namespace Cyberiada { const String QUALIFIED_NAME_SEPARATOR = "::"; +// ----------------------------------------------------------------------------- +// Geometry +// ----------------------------------------------------------------------------- + struct Point { + Point(): valid(false) {} + Point(float _x, float _y): + valid(true), x(_x), y(_y) {} + Point(CyberiadaPoint* p); + + CyberiadaPoint* c_point() const; + + bool valid; + float x, y; + }; + typedef std::list Polyline; + + struct Rect { + Rect(): valid(false) {} + Rect(float _x, float _y, float _width, float _height): + valid(true), x(_x), y(_y), width(_width), height(_height) {} + Rect(CyberiadaRect* r); + + void expand(const Point& p); + void expand(const Rect& r); + void expand(const Polyline& pl); + + CyberiadaRect* c_rect() const; + + bool operator==(const Rect& r); + + bool valid; + float x, y; + float width, height; + }; + + std::ostream& operator<<(std::ostream& os, const Point& p); + std::ostream& operator<<(std::ostream& os, const Rect& r); + std::ostream& operator<<(std::ostream& os, const Polyline& pl); + + CyberiadaPolyline* c_polyline(const Polyline& polyline); + // ----------------------------------------------------------------------------- // Base Element // ----------------------------------------------------------------------------- @@ -89,6 +130,7 @@ namespace Cyberiada { virtual int index() const; virtual bool has_geometry() const = 0; + virtual Rect get_bound_rect() const = 0; friend std::ostream& operator<<(std::ostream& os, const Element& e); virtual CyberiadaNode* to_node() const; @@ -109,42 +151,6 @@ namespace Cyberiada { }; std::ostream& operator<<(std::ostream& os, const Element& e); - -// ----------------------------------------------------------------------------- -// Geometry -// ----------------------------------------------------------------------------- - struct Point { - Point(): valid(false) {} - Point(float _x, float _y): - valid(true), x(_x), y(_y) {} - Point(CyberiadaPoint* p); - - CyberiadaPoint* c_point() const; - - bool valid; - float x, y; - }; - - struct Rect { - Rect(): valid(false) {} - Rect(float _x, float _y, float _width, float _height): - valid(true), x(_x), y(_y), width(_width), height(_height) {} - Rect(CyberiadaRect* r); - - CyberiadaRect* c_rect() const; - - bool valid; - float x, y; - float width, height; - }; - - typedef std::list Polyline; - - std::ostream& operator<<(std::ostream& os, const Point& p); - std::ostream& operator<<(std::ostream& os, const Rect& r); - std::ostream& operator<<(std::ostream& os, const Polyline& pl); - - CyberiadaPolyline* c_polyline(const Polyline& polyline); // ----------------------------------------------------------------------------- // Comment @@ -179,6 +185,7 @@ namespace Cyberiada { const Point& get_geometry_source_point() const { return source_point; } const Point& get_geometry_target_point() const { return target_point; } const Polyline& get_geometry_polyline() const { return polyline; } + Rect get_bound_rect() const; protected: std::ostream& dump(std::ostream& os) const; @@ -218,6 +225,7 @@ namespace Cyberiada { virtual bool has_geometry() const { return geometry_rect.valid; } const Rect& get_geometry_rect() const { return geometry_rect; } + virtual Rect get_bound_rect() const; virtual bool has_children() const { return false; } @@ -254,6 +262,7 @@ namespace Cyberiada { virtual bool has_geometry() const { return geometry_point.valid; } const Point& get_geometry_point() const { return geometry_point; } + virtual Rect get_bound_rect() const; virtual bool has_children() const { return false; } @@ -309,7 +318,8 @@ namespace Cyberiada { virtual bool has_geometry() const { return geometry_rect.valid; } const Rect& get_geometry_rect() const { return geometry_rect; } - + virtual Rect get_bound_rect() const; + bool has_color() const { return !color.empty(); } const Color& get_color() const { return color; } @@ -355,6 +365,7 @@ namespace Cyberiada { virtual bool has_geometry() const { return geometry_rect.valid; } const Rect& get_geometry_rect() const { return geometry_rect; } + virtual Rect get_bound_rect() const; bool has_color() const { return !color.empty(); } const Color& get_color() const { return color; } @@ -490,6 +501,7 @@ namespace Cyberiada { const Point& get_source_point() const { return source_point; } const Point& get_target_point() const { return target_point; } const Point& get_label_point() const { return label_point; } + virtual Rect get_bound_rect() const; bool has_color() const { return !color.empty(); } const Color& get_color() const { return color; } @@ -522,6 +534,8 @@ namespace Cyberiada { std::list get_transitions() const; std::list get_transitions(); + virtual Rect get_bound_rect() const; + protected: virtual std::ostream& dump(std::ostream& os) const; }; @@ -637,6 +651,9 @@ namespace Cyberiada { std::list get_state_machines(); const StateMachine* get_parent_sm(const Element* element) const; + virtual bool has_geometry() const { return false; } + virtual Rect get_bound_rect() const { return Rect(); } + protected: virtual std::ostream& dump(std::ostream& os) const; diff --git a/tests/22-bound-rect.cpp b/tests/22-bound-rect.cpp new file mode 100644 index 0000000..ee72e0f --- /dev/null +++ b/tests/22-bound-rect.cpp @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------------- + * The Cyberiada GraphML C++ library implemention + * + * The test + * + * Copyright (C) 2024 Alexey Fedoseev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/ + * ----------------------------------------------------------------------------- */ + +#include +#include "cyberiadamlpp.h" +#include "testutils.h" + +using namespace Cyberiada; +using namespace std; + +int main(int argc, char** argv) +{ + Document d; + StateMachine* sm = d.new_state_machine("SM"); + d.new_state(sm, "A", Action(), Rect(0, 50, 100, 25)); + State* s = d.new_state(sm, "B", Action(), Rect(-100, -250, 100, 250)); + d.new_state(s, "B2", Action(), Rect(0, 200, 50, 50)); + d.new_initial(s, Point(0, 0)); + d.new_state(sm, "C", Action(), Rect(-50, 0, 1000, 250)); + try { + CYB_ASSERT(sm->get_bound_rect() == Rect(-100, -250, 1050, 500)); + } catch (const Cyberiada::Exception&) { + return 1; + } + return 0; +}