From 5dd62ec86a5c69485df006081929aaa328f79447 Mon Sep 17 00:00:00 2001 From: Alexey Fedoseev Date: Sat, 27 Apr 2024 11:41:56 +0300 Subject: [PATCH] add YED geometry convertation + separate cyb string module --- Makefile | 6 +- cyb_string.c | 79 ++++++++++++ cyb_string.h | 44 +++++++ cyberiadaml.c | 323 +++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 390 insertions(+), 62 deletions(-) create mode 100644 cyb_string.c create mode 100644 cyb_string.h diff --git a/Makefile b/Makefile index 1317bd0..6ab043e 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ else endif TEST_TARGET := cyberiada_test -LIB_SOURCES := cyberiadaml.c utf8enc.c cyb_types.c +LIB_SOURCES := cyberiadaml.c utf8enc.c cyb_types.c cyb_string.c TEST_SOURCES := test.c LIB_OBJECTS := $(patsubst %.c, %.o, $(LIB_SOURCES)) TEST_OBJECTS := $(patsubst %.c, %.o, $(TEST_SOURCES)) @@ -25,11 +25,11 @@ else endif INCLUDE := -I. -I/usr/include/libxml2 -LIBS := -L/usr/lib -lxml2 +LIBS := -L/usr/lib -lxml2 -lm TEST_LIBS := -L. -lcyberiadaml $(LIB_TARGET): $(LIB_OBJECTS) -ifeq ($(DYNAMIC), 1) +ifeq ($(DYNAMIC), yes) gcc -shared $(LIBS) $(LIB_OBJECTS) -o $@ else ar rcs $@ $(LIB_OBJECTS) diff --git a/cyb_string.c b/cyb_string.c new file mode 100644 index 0000000..f112e84 --- /dev/null +++ b/cyb_string.c @@ -0,0 +1,79 @@ +/* ----------------------------------------------------------------------------- + * The Cyberiada GraphML library implemention + * + * The string utilities + * + * Copyright (C) 2024 Alexey Fedoseev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser 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 +#include + +#include "cyberiadaml.h" +#include "cyb_string.h" + +int cyberiada_copy_string(char** target, size_t* size, const char* source) +{ + char* target_str; + size_t strsize; + if (!source) { + *target = NULL; + *size = 0; + return CYBERIADA_NO_ERROR; + } + strsize = strlen(source); + if (strsize > MAX_STR_LEN - 1) { + strsize = MAX_STR_LEN - 1; + } + target_str = (char*)malloc(strsize + 1); + strncpy(target_str, source, strsize); + target_str[strsize] = 0; + *target = target_str; + if (size) { + *size = strsize; + } + return CYBERIADA_NO_ERROR; +} + +int cyberiada_string_is_empty(const char* s) +{ + while(*s) { + if (!isspace(*s)) { + return 0; + } + s++; + } + return 1; +} + +int cyberiada_string_trim(char* orig) +{ + char* s; + if (!orig) return 1; + if (!*orig) return 0; + s = orig + strlen(orig) - 1; + while(s > orig) { + if (isspace(*s)) { + *s = 0; + s--; + } else { + break; + } + } + return 0; +} diff --git a/cyb_string.h b/cyb_string.h new file mode 100644 index 0000000..8ed1604 --- /dev/null +++ b/cyb_string.h @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------------- + * The Cyberiada GraphML library implemention + * + * The string utilities + * + * Copyright (C) 2024 Alexey Fedoseev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser 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/ + * + * ----------------------------------------------------------------------------- */ + +#ifndef __CYBERIADA_STRING_H +#define __CYBERIADA_STRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------------------------- + * The Cyberiada GraphML string utilities + * ----------------------------------------------------------------------------- */ + +#define MAX_STR_LEN 4096 + + int cyberiada_copy_string(char** target, size_t* size, const char* source); + int cyberiada_string_is_empty(const char* s); + int cyberiada_string_trim(char* orig); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cyberiadaml.c b/cyberiadaml.c index 2277132..b642b3d 100644 --- a/cyberiadaml.c +++ b/cyberiadaml.c @@ -29,10 +29,12 @@ #include #include #include +#include #include "cyberiadaml.h" #include "utf8enc.h" #include "cyb_types.h" +#include "cyb_string.h" /* ----------------------------------------------------------------------------- * Cyberiada parser constants @@ -170,7 +172,6 @@ /* Misc. constants */ -#define MAX_STR_LEN 4096 #define PSEUDO_NODE_SIZE 20 #define EMPTY_TITLE "" @@ -322,61 +323,6 @@ static GraphMLKey yed_graphml_keys[] = { }; static const size_t yed_graphml_keys_count = sizeof(yed_graphml_keys) / sizeof(GraphMLKey); -/* ----------------------------------------------------------------------------- - * Utility functions - * ----------------------------------------------------------------------------- */ - -int cyberiada_copy_string(char** target, size_t* size, const char* source) -{ - char* target_str; - size_t strsize; - if (!source) { - *target = NULL; - *size = 0; - return CYBERIADA_NO_ERROR; - } - strsize = strlen(source); - if (strsize > MAX_STR_LEN - 1) { - strsize = MAX_STR_LEN - 1; - } - target_str = (char*)malloc(strsize + 1); - strncpy(target_str, source, strsize); - target_str[strsize] = 0; - *target = target_str; - if (size) { - *size = strsize; - } - return CYBERIADA_NO_ERROR; -} - -static int cyberiada_string_is_empty(const char* s) -{ - while(*s) { - if (!isspace(*s)) { - return 0; - } - s++; - } - return 1; -} - -static int cyberiada_string_trim(char* orig) -{ - char* s; - if (!orig) return 1; - if (!*orig) return 0; - s = orig + strlen(orig) - 1; - while(s > orig) { - if (isspace(*s)) { - *s = 0; - s--; - } else { - break; - } - } - return 0; -} - /* ----------------------------------------------------------------------------- * Graph manipulation functions * ----------------------------------------------------------------------------- */ @@ -511,6 +457,13 @@ static CyberiadaPoint* cyberiada_copy_point(CyberiadaPoint* src) return dst; } +static void cyberiada_update_point(CyberiadaPoint* p, float dx, float dy) +{ + if (!p) return ; + p->x += dx; + p->y += dy; +} + CyberiadaRect* cyberiada_new_rect(void) { CyberiadaRect* r = (CyberiadaRect*)malloc(sizeof(CyberiadaRect)); @@ -532,6 +485,13 @@ static CyberiadaRect* cyberiada_copy_rect(CyberiadaRect* src) return dst; } +static void cyberiada_update_rect(CyberiadaRect* r, float dx, float dy) +{ + if (!r) return ; + r->x += dx; + r->y += dy; +} + CyberiadaPolyline* cyberiada_new_polyline(void) { CyberiadaPolyline* pl = (CyberiadaPolyline*)malloc(sizeof(CyberiadaPolyline)); @@ -560,6 +520,15 @@ static CyberiadaPolyline* cyberiada_copy_polyline(CyberiadaPolyline* src) return dst; } +/*static void cyberiada_update_polyline(CyberiadaPolyline* pl, float dx, float dy) +{ + if (!pl) return ; + do { + cyberiada_update_point(&(pl->point), dx, dy); + pl = pl->next; + } while (pl); + }*/ + CyberiadaAction* cyberiada_new_action(CyberiadaActionType type, const char* trigger, const char* guard, @@ -1440,8 +1409,242 @@ static int cyberiada_graphs_reconstruct_edges(CyberiadaDocument* doc, NamesList* } return CYBERIADA_NO_ERROR; } + +static int cyberiada_graphs_reconstruct_node_geometry_from_yed(CyberiadaNode* node, float parent_x, float parent_y) +{ + float new_root_x; + float new_root_y; + + while (node) { + if (node->geometry_point) { + cyberiada_update_point(node->geometry_point, -parent_x, -parent_y); + } + + if (node->geometry_rect) { + new_root_x = node->geometry_rect->x; + new_root_y = node->geometry_rect->y; + + cyberiada_update_rect(node->geometry_rect, -parent_x, -parent_y); + } else { + new_root_x = parent_x; + new_root_y = parent_y; + } + + if (node->children) { + cyberiada_graphs_reconstruct_node_geometry_from_yed(node->children, new_root_x, new_root_y); + } + node = node->next; + } + return CYBERIADA_NO_ERROR; +} + static int cyberiada_graphs_reconstruct_geometry_from_yed(CyberiadaDocument* doc) { + CyberiadaSM* sm; + CyberiadaNode* node; + CyberiadaEdge* edge; + CyberiadaPolyline* pl; + float from_x, from_y, to_x, to_y; + float src_from_x, src_from_y, src_to_x, src_to_y, tgt_from_x, tgt_from_y, tgt_to_x, tgt_to_y; + + for (sm = doc->state_machines; sm; sm = sm->next) { + + for (edge = sm->edges; edge; edge = edge->next) { + if (edge->source && (edge->source->geometry_rect || edge->source->geometry_point) && + edge->target && (edge->target->geometry_rect || edge->target->geometry_point) && + (edge->geometry_source_point || + edge->geometry_target_point || + edge->geometry_polyline || + edge->geometry_label_point)) { + + from_x = from_y = to_x = to_y = 0.0f; + if (edge->source->geometry_point) { + from_x += edge->source->geometry_point->x; + from_y += edge->source->geometry_point->y; + } else { + from_x += edge->source->geometry_rect->x + edge->source->geometry_rect->width / 2.0f; + from_y += edge->source->geometry_rect->y + edge->source->geometry_rect->height / 2.0f; + if (edge->geometry_source_point) { + from_x += edge->geometry_source_point->x; + from_y += edge->geometry_source_point->y; + } + } + if (edge->target->geometry_point) { + to_x += edge->target->geometry_point->x; + to_y += edge->target->geometry_point->y; + } else { + to_x += edge->target->geometry_rect->x + edge->target->geometry_rect->width / 2.0f; + to_y += edge->target->geometry_rect->y + edge->target->geometry_rect->height / 2.0f; + if (edge->geometry_target_point) { + to_x += edge->geometry_target_point->x; + to_y += edge->geometry_target_point->y; + } + } + + if (edge->geometry_polyline) { + float first_p_x = edge->geometry_polyline->point.x; + float first_p_y = edge->geometry_polyline->point.y; + float last_p_x; + float last_p_y; + + pl = edge->geometry_polyline; + while (pl->next) pl = pl->next; + last_p_x = pl->point.x; + last_p_y = pl->point.y; + + src_from_x = from_x; + src_from_y = from_y; + src_to_x = first_p_x; + src_to_y = first_p_y; + tgt_from_x = last_p_x; + tgt_from_y = last_p_y; + tgt_to_x = to_x; + tgt_to_y = to_y; + } else { + src_from_x = tgt_from_x = from_x; + src_from_y = tgt_from_y = from_y; + src_to_x = tgt_to_x = to_x; + src_to_y = tgt_to_y = to_y; + } + + /* DEBUG("edge (%f; %f) -> (%f; %f) and (%f; %f) -> (%f; %f)\n", */ + /* src_from_x, src_from_y, src_to_x, src_to_y, */ + /* tgt_from_x, tgt_from_y, tgt_to_x, tgt_to_y); */ + + if (edge->geometry_source_point) { + if (edge->source->geometry_point) { + if (src_from_x == src_to_x) { + edge->geometry_source_point->x = 0.0f; + if (src_from_y >= src_to_y) { + edge->geometry_source_point->y = -PSEUDO_NODE_SIZE / 2.0f; + } else { + edge->geometry_source_point->y = PSEUDO_NODE_SIZE / 2.0f; + } + } else { + float alpha = (float)atan((src_from_y - src_to_y) / (src_to_x - src_from_x)); + edge->geometry_source_point->x = (float)cos(alpha) * PSEUDO_NODE_SIZE / 2.0f; + edge->geometry_source_point->y = -(float)sin(alpha) * PSEUDO_NODE_SIZE / 2.0f; + } + } else { + if (src_from_x == src_to_x) { + edge->geometry_source_point->x = edge->source->geometry_rect->width / 2.0f; + if (src_from_y >= src_to_y) { + edge->geometry_source_point->y = 0.0f; + } else { + edge->geometry_source_point->y = edge->source->geometry_rect->height; + } + } else { + float alpha = (float)atan((src_from_y - src_to_y) / (src_to_x - src_from_x)); + if (src_to_x < src_from_x) alpha += (float)M_PI; + float alpha_g = 180.0f * alpha / (float)M_PI; +/* DEBUG("src alpha %f\n", alpha_g);*/ + if (alpha_g < 0.0f) alpha_g += 360.0f; + if (alpha_g <= 45.0f || alpha_g > 315.0f) { + edge->geometry_source_point->x = edge->source->geometry_rect->width; + edge->geometry_source_point->y += (-(float)tan(alpha) * edge->source->geometry_rect->width + + edge->source->geometry_rect->height) / 2.0f; + } else if (alpha_g > 45.0f && alpha_g <= 135.0f) { + edge->geometry_source_point->x += ((float)tan(alpha) * edge->source->geometry_rect->height + + edge->source->geometry_rect->width) / 2.0f; + edge->geometry_source_point->y = 0.0f; + } else if (alpha_g > 135.0f && alpha_g <= 225.0f) { + edge->geometry_source_point->x = 0.0f; + edge->geometry_source_point->y += (-(float)tan(alpha) * edge->source->geometry_rect->width + + edge->source->geometry_rect->height) / 2.0f; + } else { + edge->geometry_source_point->x += (-(float)tan(alpha) * edge->source->geometry_rect->height + + edge->source->geometry_rect->width) / 2.0f; + edge->geometry_source_point->y = edge->source->geometry_rect->height; + } + + if (edge->geometry_source_point->x < 0) { + edge->geometry_source_point->x = 0.0f; + } + if (edge->geometry_source_point->x > edge->source->geometry_rect->width) { + edge->geometry_source_point->x = edge->source->geometry_rect->width; + } + if (edge->geometry_source_point->y < 0) { + edge->geometry_source_point->y = 0.0f; + } + if (edge->geometry_source_point->y > edge->source->geometry_rect->height) { + edge->geometry_source_point->y = edge->source->geometry_rect->height; + } + } + } + /* DEBUG("sp: (%f; %f)\n", edge->geometry_source_point->x, edge->geometry_source_point->y); */ + } + + if (edge->geometry_target_point) { + if (edge->target->geometry_point) { + if (tgt_from_x == tgt_to_x) { + edge->geometry_target_point->x = 0.0f; + if (tgt_from_y >= tgt_to_y) { + edge->geometry_target_point->y = PSEUDO_NODE_SIZE / 2.0f; + } else { + edge->geometry_target_point->y = -PSEUDO_NODE_SIZE / 2.0f; + } + } else { + float alpha = (float)atan((tgt_from_y - tgt_to_y) / (tgt_to_x - tgt_from_x)); + edge->geometry_target_point->x = (float)cos(alpha) * PSEUDO_NODE_SIZE / 2.0f; + edge->geometry_target_point->y = -(float)sin(alpha) * PSEUDO_NODE_SIZE / 2.0f; + } + } else { + if (tgt_from_x == tgt_to_x) { + edge->geometry_target_point->x = edge->target->geometry_rect->width / 2.0f; + if (tgt_from_y >= tgt_to_y) { + edge->geometry_target_point->y = edge->target->geometry_rect->height; + } else { + edge->geometry_target_point->y = 0.0f; + } + } else { + float alpha = (float)atan((tgt_from_y - tgt_to_y) / (tgt_to_x - tgt_from_x)); + if (tgt_to_x < tgt_from_x) alpha += (float)M_PI; + alpha += (float)M_PI; /* target = incoming edge */ + float alpha_g = 180.0f * alpha / (float)M_PI; +/* DEBUG("tgt alpha %f\n", alpha_g);*/ + if (alpha_g < 0.0f) alpha_g += 360.0f; + if (alpha_g <= 45.0f || alpha_g > 315.0f) { + edge->geometry_target_point->x = edge->target->geometry_rect->width; + edge->geometry_target_point->y += (-(float)tan(alpha) * edge->target->geometry_rect->width + + edge->target->geometry_rect->height) / 2.0f; + } else if (alpha_g > 45.0f && alpha_g <= 135.0f) { + edge->geometry_target_point->x += ((float)tan(alpha) * edge->target->geometry_rect->height + + edge->target->geometry_rect->width) / 2.0f; + edge->geometry_target_point->y = 0.0f; + } else if (alpha_g > 135.0f && alpha_g <= 225.0f) { + edge->geometry_target_point->x = 0.0f; + edge->geometry_target_point->y += (-(float)tan(alpha) * edge->target->geometry_rect->width + + edge->target->geometry_rect->height) / 2.0f; + } else { + edge->geometry_target_point->x += (-(float)tan(alpha) * edge->target->geometry_rect->height + + edge->target->geometry_rect->width) / 2.0f; + edge->geometry_target_point->y = edge->target->geometry_rect->height; + } + } + + if (edge->geometry_target_point->x < 0) { + edge->geometry_target_point->x = 0.0f; + } + if (edge->geometry_target_point->x > edge->target->geometry_rect->width) { + edge->geometry_target_point->x = edge->target->geometry_rect->width; + } + if (edge->geometry_target_point->y < 0) { + edge->geometry_target_point->y = 0.0f; + } + if (edge->geometry_target_point->y > edge->target->geometry_rect->height) { + edge->geometry_target_point->y = edge->target->geometry_rect->height; + } + } + /* DEBUG("tp: (%f; %f)\n", edge->geometry_target_point->x, edge->geometry_target_point->y); */ + } + } + } + + for (node = sm->nodes; node; node = node->next) { + cyberiada_graphs_reconstruct_node_geometry_from_yed(node, 0.0f, 0.0f); + } + } + return CYBERIADA_NO_ERROR; } @@ -3306,9 +3509,6 @@ int cyberiada_read_sm_document(CyberiadaDocument* cyb_doc, const char* filename, /* DEBUG("reading format %d\n", format); */ if (format == cybxmlYED) { res = cyberiada_decode_yed_xml(root, cyb_doc); - if (res != CYBERIADA_NO_ERROR) { - cyberiada_graphs_reconstruct_geometry_from_yed(cyb_doc); - } } else if (format == cybxmlCyberiada10) { res = cyberiada_decode_cyberiada_xml(root, cyb_doc); } else { @@ -3335,6 +3535,11 @@ int cyberiada_read_sm_document(CyberiadaDocument* cyb_doc, const char* filename, filename); break; } + + if (format == cybxmlYED && res == CYBERIADA_NO_ERROR) { + cyberiada_graphs_reconstruct_geometry_from_yed(cyb_doc); + } + } while(0); cyberiada_free_name_list(&nl);