calculate SM bound rectangles + test

This commit is contained in:
Alexey Fedoseev
2024-05-11 10:33:24 +03:00
parent 630ac70c2e
commit 6bcfd52b07
3 changed files with 237 additions and 41 deletions

View File

@@ -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<CommentSubject>::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<Transition*> 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);

View File

@@ -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<Point> 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;
@@ -110,42 +152,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<Point> 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,6 +318,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; }
@@ -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<const Transition*> get_transitions() const;
std::list<Transition*> 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<StateMachine*> 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;

44
tests/22-bound-rect.cpp Normal file
View File

@@ -0,0 +1,44 @@
/* -----------------------------------------------------------------------------
* The Cyberiada GraphML C++ library implemention
*
* The test
*
* Copyright (C) 2024 Alexey Fedoseev <aleksey@fedoseev.net>
*
* 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 <iostream>
#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;
}