new geometry implementation + API update
This commit is contained in:
@@ -7,7 +7,9 @@ if(!LibXml2_FOUND)
|
||||
message(FATAL_ERROR "Cannot find libxml2 library")
|
||||
endif()
|
||||
|
||||
add_library(cyberiadaml SHARED cyberiadaml.c utf8enc.c cyb_types.c cyb_string.c)
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D__DEBUG__")
|
||||
|
||||
add_library(cyberiadaml SHARED cyberiadaml.c utf8enc.c cyb_types.c cyb_string.c geometry.c)
|
||||
target_include_directories(cyberiadaml PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include/cyberiada>
|
||||
|
||||
38
cyb_error.h
Normal file
38
cyb_error.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
* The Cyberiada GraphML library implemention
|
||||
*
|
||||
* Error handling
|
||||
*
|
||||
* 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 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_ERROR_H
|
||||
#define __CYBERIADA_ERROR_H
|
||||
|
||||
#ifdef __DEBUG__
|
||||
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG(...)
|
||||
#endif
|
||||
|
||||
#ifndef __SILENT__
|
||||
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define ERROR(...)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
52
cyb_types.c
52
cyb_types.c
@@ -38,7 +38,7 @@ int cyberiada_stack_update_top_key(CyberiadaStack** stack, const char* new_key
|
||||
if (!stack || !*stack) {
|
||||
return -1;
|
||||
}
|
||||
(*stack)->key = new_key;
|
||||
(*stack)->key = (void*)new_key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ int cyberiada_list_add(CyberiadaList** list, const char* key, void* data)
|
||||
return -1;
|
||||
}
|
||||
new_item = (CyberiadaList*)malloc(sizeof(CyberiadaList));
|
||||
new_item->key = key;
|
||||
new_item->key = (void*)key;
|
||||
new_item->data = data;
|
||||
new_item->next = NULL;
|
||||
if (!*list) {
|
||||
@@ -119,7 +119,7 @@ void* cyberiada_list_find(CyberiadaList** list, const char* key)
|
||||
}
|
||||
item = *list;
|
||||
while (item) {
|
||||
if (item->key && strcmp(item->key, key) == 0) {
|
||||
if (item->key && strcmp((const char*)item->key, key) == 0) {
|
||||
return item->data;
|
||||
}
|
||||
item = item->next;
|
||||
@@ -140,3 +140,49 @@ int cyberiada_list_free(CyberiadaList** list)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cyberiada_queue_add(CyberiadaQueue** queue, void* key, void* data)
|
||||
{
|
||||
CyberiadaQueue* new_item = (CyberiadaQueue*)malloc(sizeof(CyberiadaQueue));
|
||||
memset(new_item, 0, sizeof(CyberiadaQueue));
|
||||
new_item->key = key;
|
||||
new_item->data = data;
|
||||
new_item->next = (*queue);
|
||||
*queue = new_item;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cyberiada_queue_get(CyberiadaQueue** queue, void** key, void** data)
|
||||
{
|
||||
CyberiadaQueue *q, *prev = NULL;
|
||||
if (!queue || !key || !data) return 1;
|
||||
q = *queue;
|
||||
while (q->next) {
|
||||
prev = q;
|
||||
q = q->next;
|
||||
}
|
||||
*key = q->key;
|
||||
*data = q->data;
|
||||
free(q);
|
||||
if (prev) {
|
||||
prev->next = NULL;
|
||||
} else {
|
||||
*queue = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cyberiada_queue_free(CyberiadaQueue** queue)
|
||||
{
|
||||
CyberiadaQueue* item;
|
||||
if (!queue) {
|
||||
return 0;
|
||||
}
|
||||
while(*queue) {
|
||||
item = *queue;
|
||||
*queue = item->next;
|
||||
free(item);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ extern "C" {
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
struct _CyberiadaStruct {
|
||||
const char* key;
|
||||
void* key;
|
||||
void* data;
|
||||
struct _CyberiadaStruct* next;
|
||||
};
|
||||
@@ -52,6 +52,12 @@ extern "C" {
|
||||
int cyberiada_list_add(CyberiadaList** list, const char* key, void* data);
|
||||
void* cyberiada_list_find(CyberiadaList** list, const char* key);
|
||||
int cyberiada_list_free(CyberiadaList** list);
|
||||
|
||||
typedef struct _CyberiadaStruct CyberiadaQueue;
|
||||
|
||||
int cyberiada_queue_add(CyberiadaQueue** queue, void* key, void* data);
|
||||
int cyberiada_queue_get(CyberiadaQueue** queue, void** key, void**data);
|
||||
int cyberiada_queue_free(CyberiadaQueue** queue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
669
cyberiadaml.c
669
cyberiadaml.c
@@ -35,6 +35,8 @@
|
||||
#include "utf8enc.h"
|
||||
#include "cyb_types.h"
|
||||
#include "cyb_string.h"
|
||||
#include "geometry.h"
|
||||
#include "cyb_error.h"
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Cyberiada parser constants
|
||||
@@ -173,16 +175,9 @@
|
||||
/* Misc. constants */
|
||||
|
||||
#define PSEUDO_NODE_SIZE 20
|
||||
#define DEFAULT_NODE_SIZE 100
|
||||
#define EMPTY_TITLE ""
|
||||
|
||||
#ifdef __DEBUG__
|
||||
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG(...)
|
||||
#endif
|
||||
|
||||
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
/* Types & macros for XML/GraphML processing */
|
||||
|
||||
#define INDENT_STR " "
|
||||
@@ -231,6 +226,7 @@
|
||||
fprintf(stderr, "xml close element error %d at %s:%d", res, __FILE__, __LINE__); \
|
||||
return CYBERIADA_XML_ERROR; \
|
||||
}
|
||||
# define XML_READMEMORY_BASENAME "noname.xml"
|
||||
|
||||
typedef struct {
|
||||
char* attr_id;
|
||||
@@ -438,97 +434,6 @@ static CyberiadaLink* cyberiada_copy_link(CyberiadaLink* src)
|
||||
return cyberiada_new_link(src->ref);
|
||||
}
|
||||
|
||||
CyberiadaPoint* cyberiada_new_point(void)
|
||||
{
|
||||
CyberiadaPoint* p = (CyberiadaPoint*)malloc(sizeof(CyberiadaPoint));
|
||||
memset(p, 0, sizeof(CyberiadaPoint));
|
||||
return p;
|
||||
}
|
||||
|
||||
static CyberiadaPoint* cyberiada_copy_point(CyberiadaPoint* src)
|
||||
{
|
||||
CyberiadaPoint* dst;
|
||||
if (!src) {
|
||||
return NULL;
|
||||
}
|
||||
dst = cyberiada_new_point();
|
||||
dst->x = src->x;
|
||||
dst->y = src->y;
|
||||
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));
|
||||
memset(r, 0, sizeof(CyberiadaRect));
|
||||
return r;
|
||||
}
|
||||
|
||||
static CyberiadaRect* cyberiada_copy_rect(CyberiadaRect* src)
|
||||
{
|
||||
CyberiadaRect* dst;
|
||||
if (!src) {
|
||||
return NULL;
|
||||
}
|
||||
dst = cyberiada_new_rect();
|
||||
dst->x = src->x;
|
||||
dst->y = src->y;
|
||||
dst->width = src->width;
|
||||
dst->height = src->height;
|
||||
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));
|
||||
memset(pl, 0, sizeof(CyberiadaPolyline));
|
||||
return pl;
|
||||
}
|
||||
|
||||
static CyberiadaPolyline* cyberiada_copy_polyline(CyberiadaPolyline* src)
|
||||
{
|
||||
CyberiadaPolyline *dst = NULL, *prev, *pl;
|
||||
if (!src) {
|
||||
return NULL;
|
||||
}
|
||||
do {
|
||||
pl = cyberiada_new_polyline();
|
||||
pl->point.x = src->point.x;
|
||||
pl->point.y = src->point.y;
|
||||
if (dst) {
|
||||
prev->next = pl;
|
||||
} else {
|
||||
dst = pl;
|
||||
}
|
||||
prev = pl;
|
||||
src = src->next;
|
||||
} while (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,
|
||||
@@ -545,21 +450,10 @@ CyberiadaAction* cyberiada_new_action(CyberiadaActionType type,
|
||||
|
||||
static CyberiadaAction* cyberiada_copy_action(CyberiadaAction* src)
|
||||
{
|
||||
CyberiadaAction *dst = NULL, *prev, *a;
|
||||
if (!src) {
|
||||
return NULL;
|
||||
}
|
||||
do {
|
||||
a = cyberiada_new_action(src->type, src->trigger, src->guard, src->behavior);
|
||||
if (dst) {
|
||||
prev->next = a;
|
||||
} else {
|
||||
dst = a;
|
||||
}
|
||||
prev = a;
|
||||
src = src->next;
|
||||
} while (src);
|
||||
return dst;
|
||||
return cyberiada_new_action(src->type, src->trigger, src->guard, src->behavior);
|
||||
}
|
||||
|
||||
CyberiadaNode* cyberiada_new_node(const char* id)
|
||||
@@ -1256,7 +1150,6 @@ static CyberiadaEdge* cyberiada_graph_find_last_edge(CyberiadaSM* sm)
|
||||
|
||||
static int cyberiada_destroy_edge(CyberiadaEdge* e)
|
||||
{
|
||||
CyberiadaPolyline *polyline, *pl;
|
||||
if (e->id) free(e->id);
|
||||
if (e->source_id) free(e->source_id);
|
||||
if (e->target_id) free(e->target_id);
|
||||
@@ -1266,12 +1159,7 @@ static int cyberiada_destroy_edge(CyberiadaEdge* e)
|
||||
free(e->comment_subject);
|
||||
}
|
||||
if (e->geometry_polyline) {
|
||||
polyline = e->geometry_polyline;
|
||||
do {
|
||||
pl = polyline;
|
||||
polyline = polyline->next;
|
||||
free(pl);
|
||||
} while (polyline);
|
||||
cyberiada_destroy_polyline(e->geometry_polyline);
|
||||
}
|
||||
if (e->geometry_source_point) free(e->geometry_source_point);
|
||||
if (e->geometry_target_point) free(e->geometry_target_point);
|
||||
@@ -1311,7 +1199,7 @@ static void cyberiada_free_name_list(NamesList** nl)
|
||||
cyberiada_list_free(nl);
|
||||
}
|
||||
|
||||
static int cyberiada_graphs_reconstruct_nodes(CyberiadaNode* root, NamesList** nl)
|
||||
static int cyberiada_graphs_reconstruct_node_identifiers(CyberiadaNode* root, NamesList** nl)
|
||||
{
|
||||
char buffer[MAX_STR_LEN];
|
||||
size_t buffer_len = sizeof(buffer) - 1;
|
||||
@@ -1340,14 +1228,14 @@ static int cyberiada_graphs_reconstruct_nodes(CyberiadaNode* root, NamesList** n
|
||||
cyberiada_add_name_to_list(nl, "", node->id);
|
||||
}
|
||||
if (node->children) {
|
||||
cyberiada_graphs_reconstruct_nodes(node->children, nl);
|
||||
cyberiada_graphs_reconstruct_node_identifiers(node->children, nl);
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return CYBERIADA_NO_ERROR;
|
||||
}
|
||||
|
||||
static int cyberiada_graphs_reconstruct_edges(CyberiadaDocument* doc, NamesList** nl)
|
||||
static int cyberiada_graphs_reconstruct_edge_identifiers(CyberiadaDocument* doc, NamesList** nl)
|
||||
{
|
||||
char buffer[MAX_STR_LEN];
|
||||
size_t buffer_len = sizeof(buffer) - 1;
|
||||
@@ -1410,249 +1298,6 @@ 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;
|
||||
}
|
||||
|
||||
static int cyberiada_graphs_reconstruct_geometry_to_yed(CyberiadaDocument* doc)
|
||||
{
|
||||
return CYBERIADA_NO_ERROR;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
size_t value_offset;
|
||||
@@ -1961,20 +1606,10 @@ static int cyberiada_decode_meta(CyberiadaDocument* doc, char* metadata)
|
||||
return CYBERIADA_NO_ERROR;
|
||||
}
|
||||
|
||||
static int cyberiada_init_sm(CyberiadaSM* sm)
|
||||
{
|
||||
if (sm) {
|
||||
sm->nodes = NULL;
|
||||
sm->edges = NULL;
|
||||
sm->next = NULL;
|
||||
}
|
||||
return CYBERIADA_NO_ERROR;
|
||||
}
|
||||
|
||||
CyberiadaSM* cyberiada_new_sm(void)
|
||||
{
|
||||
CyberiadaSM* sm = (CyberiadaSM*)malloc(sizeof(CyberiadaSM));
|
||||
cyberiada_init_sm(sm);
|
||||
memset(sm, 0, sizeof(CyberiadaSM));
|
||||
return sm;
|
||||
}
|
||||
|
||||
@@ -1982,6 +1617,9 @@ static int cyberiada_destroy_sm(CyberiadaSM* sm)
|
||||
{
|
||||
CyberiadaEdge *edge, *e;
|
||||
if (sm) {
|
||||
if (sm->bounding_rect) {
|
||||
free(sm->bounding_rect);
|
||||
}
|
||||
if (sm->nodes) {
|
||||
cyberiada_destroy_all_nodes(sm->nodes);
|
||||
}
|
||||
@@ -2266,7 +1904,7 @@ static int cyberiada_get_element_text(char* buffer, size_t buffer_len,
|
||||
|
||||
static int cyberiada_xml_read_coord(xmlNode* xml_node,
|
||||
const char* attr_name,
|
||||
float* result)
|
||||
double* result)
|
||||
{
|
||||
char buffer[MAX_STR_LEN];
|
||||
size_t buffer_len = sizeof(buffer) - 1;
|
||||
@@ -2275,7 +1913,7 @@ static int cyberiada_xml_read_coord(xmlNode* xml_node,
|
||||
attr_name) != CYBERIADA_NO_ERROR) {
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
*result = (float)atof(buffer);
|
||||
*result = (double)atof(buffer);
|
||||
return CYBERIADA_NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -2528,8 +2166,8 @@ static GraphProcessorState handle_node_geometry(xmlNode* xml_node,
|
||||
}
|
||||
if (type == cybNodeInitial) {
|
||||
current->geometry_point = cyberiada_new_point();
|
||||
current->geometry_point->x = rect->x + rect->width / 2.0f;
|
||||
current->geometry_point->y = rect->y + rect->height / 2.0f;
|
||||
current->geometry_point->x = rect->x + rect->width / 2.0;
|
||||
current->geometry_point->y = rect->y + rect->height / 2.0;
|
||||
free(rect);
|
||||
return gpsNodeStart;
|
||||
} else if (type == cybNodeComment) {
|
||||
@@ -2574,8 +2212,10 @@ static GraphProcessorState handle_node_title(xmlNode* xml_node,
|
||||
return gpsInvalid;
|
||||
}
|
||||
cyberiada_get_element_text(buffer, buffer_len, xml_node);
|
||||
/* DEBUG("Set node %s title %s\n", current->id, buffer); */
|
||||
DEBUG("Set node %s title '%s'\n", current->id, buffer);
|
||||
cyberiada_copy_string(&(current->title), &(current->title_len), buffer);
|
||||
cyberiada_string_trim(current->title);
|
||||
DEBUG("After trim: '%s'\n", current->title);
|
||||
return gpsNodeAction;
|
||||
}
|
||||
|
||||
@@ -2658,7 +2298,7 @@ static GraphProcessorState handle_edge_label(xmlNode* xml_node,
|
||||
{
|
||||
char buffer[MAX_STR_LEN];
|
||||
size_t buffer_len = sizeof(buffer) - 1;
|
||||
float x = 0.0, y = 0.0;
|
||||
double x = 0.0, y = 0.0;
|
||||
CyberiadaEdge *current;
|
||||
CyberiadaSM* sm = doc->state_machines;
|
||||
while (sm->next) sm = sm->next;
|
||||
@@ -2870,6 +2510,7 @@ static GraphProcessorState handle_node_data(xmlNode* xml_node,
|
||||
}
|
||||
/* DEBUG("Set node %s title %s\n", current->id, buffer); */
|
||||
cyberiada_copy_string(&(current->title), &(current->title_len), buffer);
|
||||
cyberiada_string_trim(current->title);
|
||||
} else if (strcmp(key_name, GRAPHML_CYB_KEY_STATE_MACHINE_NAME) == 0) {
|
||||
if (current->type != cybNodeSM) {
|
||||
ERROR("Using state machine key outside the graph element in %s\n", current->id);
|
||||
@@ -3466,40 +3107,74 @@ static int cyberiada_check_graphml_ns(xmlNode* root, CyberiadaXMLFormat* format)
|
||||
/* -----------------------------------------------------------------------------
|
||||
* GraphML reader interface
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
int cyberiada_read_sm_document(CyberiadaDocument* cyb_doc, const char* filename, CyberiadaXMLFormat format)
|
||||
static int cyberiada_process_decode_sm_document(CyberiadaDocument* cyb_doc, xmlDoc* doc, CyberiadaXMLFormat format, int flags)
|
||||
{
|
||||
int res;
|
||||
xmlDoc* doc = NULL;
|
||||
xmlNode* root = NULL;
|
||||
CyberiadaSM* sm;
|
||||
NamesList* nl = NULL;
|
||||
int geom_flags;
|
||||
|
||||
if (flags & CYBERIADA_FLAG_ROUND_GEOMETRY) {
|
||||
ERROR("Round geometry flag is not supported on import\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (flags & CYBERIADA_FLAG_SKIP_GEOMETRY &&
|
||||
flags != CYBERIADA_FLAG_SKIP_GEOMETRY) {
|
||||
ERROR("The skip geometry flag is not compatible with other flags\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
geom_flags = flags & (CYBERIADA_FLAG_ABSOLUTE_GEOMETRY |
|
||||
CYBERIADA_FLAG_LEFTTOP_LOCAL_GEOMETRY |
|
||||
CYBERIADA_FLAG_CENTER_LOCAL_GEOMETRY);
|
||||
if (geom_flags &&
|
||||
geom_flags != CYBERIADA_FLAG_ABSOLUTE_GEOMETRY &&
|
||||
geom_flags != CYBERIADA_FLAG_LEFTTOP_LOCAL_GEOMETRY &&
|
||||
geom_flags != CYBERIADA_FLAG_CENTER_LOCAL_GEOMETRY) {
|
||||
ERROR("Single geometry flag (abs, left-top, center) can be used at the same time\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (!(flags & CYBERIADA_FLAG_SKIP_GEOMETRY) && !geom_flags) {
|
||||
/* set default geometry flag */
|
||||
flags |= CYBERIADA_FLAG_CENTER_LOCAL_GEOMETRY;
|
||||
}
|
||||
|
||||
geom_flags = flags & (CYBERIADA_FLAG_CENTER_EDGE_GEOMETRY |
|
||||
CYBERIADA_FLAG_LEFTTOP_BORDER_EDGE_GEOMETRY |
|
||||
CYBERIADA_FLAG_CENTER_BORDER_EDGE_GEOMETRY);
|
||||
if (geom_flags &&
|
||||
geom_flags != CYBERIADA_FLAG_CENTER_EDGE_GEOMETRY &&
|
||||
geom_flags != CYBERIADA_FLAG_LEFTTOP_BORDER_EDGE_GEOMETRY &&
|
||||
geom_flags != CYBERIADA_FLAG_CENTER_BORDER_EDGE_GEOMETRY) {
|
||||
ERROR("Single geometry edge flag (border, center) can be used at the same time\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (!(flags & CYBERIADA_FLAG_SKIP_GEOMETRY) && !geom_flags) {
|
||||
/* set default edge geometry flag */
|
||||
flags |= CYBERIADA_FLAG_CENTER_BORDER_EDGE_GEOMETRY;
|
||||
}
|
||||
|
||||
cyberiada_init_sm_document(cyb_doc);
|
||||
cyberiada_init_action_regexps();
|
||||
xmlInitParser();
|
||||
|
||||
do {
|
||||
|
||||
/* parse the file and get the DOM */
|
||||
if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
|
||||
ERROR("error: could not parse file %s\n", filename);
|
||||
res = CYBERIADA_XML_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* get the root element node */
|
||||
root = xmlDocGetRootElement(doc);
|
||||
|
||||
if (strcmp((const char*)root->name, GRAPHML_GRAPHML_ELEMENT) != 0) {
|
||||
ERROR("error: could not find GraphML root node %s\n", filename);
|
||||
ERROR("error: could not find GraphML root node\n");
|
||||
res = CYBERIADA_XML_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* check whether the xml is graphml */
|
||||
if (cyberiada_check_graphml_ns(root, &format)) {
|
||||
ERROR("error: no valid graphml namespace in %s\n", filename);
|
||||
ERROR("error: no valid graphml namespace\n");
|
||||
res = CYBERIADA_XML_ERROR;
|
||||
break;
|
||||
}
|
||||
@@ -3512,8 +3187,7 @@ int cyberiada_read_sm_document(CyberiadaDocument* cyb_doc, const char* filename,
|
||||
} else if (format == cybxmlCyberiada10) {
|
||||
res = cyberiada_decode_cyberiada_xml(root, cyb_doc);
|
||||
} else {
|
||||
ERROR("error: unsupported GraphML format of file %s\n",
|
||||
filename);
|
||||
ERROR("error: unsupported GraphML format of file\n");
|
||||
res = CYBERIADA_XML_ERROR;
|
||||
break;
|
||||
}
|
||||
@@ -3523,31 +3197,77 @@ int cyberiada_read_sm_document(CyberiadaDocument* cyb_doc, const char* filename,
|
||||
}
|
||||
|
||||
for (sm = cyb_doc->state_machines; sm; sm = sm->next) {
|
||||
if ((res = cyberiada_graphs_reconstruct_nodes(sm->nodes, &nl)) != CYBERIADA_NO_ERROR) {
|
||||
ERROR("error: cannot reconstruct graph nodes file %s\n",
|
||||
filename);
|
||||
if ((res = cyberiada_graphs_reconstruct_node_identifiers(sm->nodes, &nl)) != CYBERIADA_NO_ERROR) {
|
||||
ERROR("error: cannot reconstruct graph nodes' indentifiers\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((res = cyberiada_graphs_reconstruct_edges(cyb_doc, &nl)) != CYBERIADA_NO_ERROR) {
|
||||
ERROR("error: cannot reconstruct graph edges from file %s\n",
|
||||
filename);
|
||||
if ((res = cyberiada_graphs_reconstruct_edge_identifiers(cyb_doc, &nl)) != CYBERIADA_NO_ERROR) {
|
||||
ERROR("error: cannot reconstruct graph edges' identifier\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (format == cybxmlYED && res == CYBERIADA_NO_ERROR) {
|
||||
cyberiada_graphs_reconstruct_geometry_from_yed(cyb_doc);
|
||||
|
||||
if (res == CYBERIADA_NO_ERROR) {
|
||||
if (flags & CYBERIADA_FLAG_SKIP_GEOMETRY) {
|
||||
cyberiada_clean_document_geometry(cyb_doc);
|
||||
} else {
|
||||
cyberiada_import_document_geometry(cyb_doc, flags, format);
|
||||
}
|
||||
}
|
||||
|
||||
} while(0);
|
||||
|
||||
cyberiada_free_name_list(&nl);
|
||||
cyberiada_free_action_regexps();
|
||||
xmlFreeDoc(doc);
|
||||
xmlCleanupParser();
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
int cyberiada_read_sm_document(CyberiadaDocument* cyb_doc, const char* filename,
|
||||
CyberiadaXMLFormat format, int flags)
|
||||
{
|
||||
int res;
|
||||
xmlDoc* doc = NULL;
|
||||
xmlInitParser();
|
||||
|
||||
/* parse the file and get the DOM */
|
||||
if ((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
|
||||
ERROR("error: could not parse file %s\n", filename);
|
||||
xmlCleanupParser();
|
||||
return CYBERIADA_XML_ERROR;
|
||||
}
|
||||
|
||||
res = cyberiada_process_decode_sm_document(cyb_doc, doc, format, flags);
|
||||
|
||||
if (doc) {
|
||||
xmlFreeDoc(doc);
|
||||
}
|
||||
xmlCleanupParser();
|
||||
return res;
|
||||
}
|
||||
|
||||
int cyberiada_decode_sm_document(CyberiadaDocument* cyb_doc, const char* buffer, size_t buffer_size,
|
||||
CyberiadaXMLFormat format, int flags)
|
||||
{
|
||||
int res;
|
||||
xmlDoc* doc = NULL;
|
||||
xmlInitParser();
|
||||
|
||||
/* parse the file and get the DOM */
|
||||
if ((doc = xmlReadMemory(buffer, buffer_size, XML_READMEMORY_BASENAME, NULL, 0)) == NULL) {
|
||||
ERROR("error: could not read buffer\n");
|
||||
xmlCleanupParser();
|
||||
return CYBERIADA_XML_ERROR;
|
||||
}
|
||||
|
||||
res = cyberiada_process_decode_sm_document(cyb_doc, doc, format, flags);
|
||||
|
||||
if (doc) {
|
||||
xmlFreeDoc(doc);
|
||||
}
|
||||
xmlCleanupParser();
|
||||
return res;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@@ -4695,11 +4415,38 @@ static int cyberiada_write_sm_document_yed(CyberiadaDocument* doc, xmlTextWriter
|
||||
* GraphML writer interface
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
int cyberiada_write_sm_document(CyberiadaDocument* doc, const char* filename, CyberiadaXMLFormat format)
|
||||
static int cyberiada_process_encode_sm_document(CyberiadaDocument* doc, xmlTextWriterPtr writer,
|
||||
CyberiadaXMLFormat format, int flags)
|
||||
{
|
||||
CyberiadaDocument* copy_doc = NULL;
|
||||
xmlTextWriterPtr writer;
|
||||
int res;
|
||||
|
||||
if (flags & CYBERIADA_FLAG_RECONSTRUCT_GEOMETRY) {
|
||||
ERROR("Geometry reconstructioin flag is not supported on export\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (flags & CYBERIADA_FLAG_SKIP_GEOMETRY) {
|
||||
if (flags != CYBERIADA_FLAG_SKIP_GEOMETRY) {
|
||||
ERROR("The skip geometry flag is not compatible with other flags\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
if (format == cybxmlYED) {
|
||||
ERROR("Skip geometry flag is not allowed for YED export\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (CYBERIADA_FLAG_ABSOLUTE_GEOMETRY |
|
||||
CYBERIADA_FLAG_LEFTTOP_LOCAL_GEOMETRY |
|
||||
CYBERIADA_FLAG_CENTER_LOCAL_GEOMETRY |
|
||||
CYBERIADA_FLAG_LEFTTOP_BORDER_EDGE_GEOMETRY |
|
||||
CYBERIADA_FLAG_CENTER_BORDER_EDGE_GEOMETRY |
|
||||
CYBERIADA_FLAG_CENTER_EDGE_GEOMETRY) {
|
||||
|
||||
ERROR("Geometry flags (abs, left-top, center) & edge geometry flags are not allowed for export\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (format != cybxmlCyberiada10 && format != cybxmlYED) {
|
||||
ERROR("unsupported SM format for write: %d\n", format);
|
||||
@@ -4711,16 +4458,12 @@ int cyberiada_write_sm_document(CyberiadaDocument* doc, const char* filename, Cy
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
xmlInitParser();
|
||||
|
||||
if (format == cybxmlYED && (!doc->state_machines ||doc->state_machines->next)) {
|
||||
ERROR("YED format supports only single SM documents\n");
|
||||
return CYBERIADA_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
writer = xmlNewTextWriterFilename(filename, 0);
|
||||
if (!writer) {
|
||||
ERROR("cannot open xml writter for file %s\n", filename);
|
||||
res = CYBERIADA_XML_ERROR;
|
||||
}
|
||||
|
||||
res = xmlTextWriterStartDocument(writer, NULL, GRAPHML_XML_ENCODING, NULL);
|
||||
if (res < 0) {
|
||||
ERROR("error writing xml start document: %d\n", res);
|
||||
@@ -4728,22 +4471,20 @@ int cyberiada_write_sm_document(CyberiadaDocument* doc, const char* filename, Cy
|
||||
break;
|
||||
}
|
||||
|
||||
copy_doc = cyberiada_copy_sm_document(doc);
|
||||
|
||||
if (flags & CYBERIADA_FLAG_SKIP_GEOMETRY) {
|
||||
cyberiada_clean_document_geometry(copy_doc);
|
||||
} else {
|
||||
cyberiada_export_document_geometry(copy_doc, flags, format);
|
||||
}
|
||||
if (format == cybxmlYED) {
|
||||
if (!doc->state_machines || doc->state_machines->next) {
|
||||
ERROR("YED format supports only single SM documents\n");
|
||||
res = CYBERIADA_BAD_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
copy_doc = cyberiada_copy_sm_document(doc);
|
||||
cyberiada_graphs_reconstruct_geometry_to_yed(copy_doc);
|
||||
|
||||
res = cyberiada_write_sm_document_yed(copy_doc, writer);
|
||||
|
||||
cyberiada_destroy_sm_document(copy_doc);
|
||||
} else if (format == cybxmlCyberiada10) {
|
||||
res = cyberiada_write_sm_document_cyberiada(doc, writer);
|
||||
res = cyberiada_write_sm_document_cyberiada(copy_doc, writer);
|
||||
}
|
||||
cyberiada_destroy_sm_document(copy_doc);
|
||||
|
||||
if (res != CYBERIADA_NO_ERROR) {
|
||||
ERROR("error writing xml %d\n", res);
|
||||
res = CYBERIADA_XML_ERROR;
|
||||
@@ -4760,12 +4501,72 @@ int cyberiada_write_sm_document(CyberiadaDocument* doc, const char* filename, Cy
|
||||
res = CYBERIADA_NO_ERROR;
|
||||
} while (0);
|
||||
|
||||
xmlFreeTextWriter(writer);
|
||||
xmlCleanupParser();
|
||||
|
||||
if (res != CYBERIADA_NO_ERROR) {
|
||||
return res;
|
||||
} else {
|
||||
return CYBERIADA_NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int cyberiada_write_sm_document(CyberiadaDocument* doc, const char* filename,
|
||||
CyberiadaXMLFormat format, int flags)
|
||||
{
|
||||
int res;
|
||||
xmlTextWriterPtr writer = NULL;
|
||||
xmlInitParser();
|
||||
writer = xmlNewTextWriterFilename(filename, 0);
|
||||
if (!writer) {
|
||||
ERROR("cannot open xml writter for file %s\n", filename);
|
||||
xmlCleanupParser();
|
||||
return CYBERIADA_XML_ERROR;
|
||||
}
|
||||
|
||||
res = cyberiada_process_encode_sm_document(doc, writer, format, flags);
|
||||
|
||||
xmlFreeTextWriter(writer);
|
||||
xmlCleanupParser();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int cyberiada_encode_sm_document(CyberiadaDocument* doc, char** buffer, size_t* buffer_size,
|
||||
CyberiadaXMLFormat format, int flags)
|
||||
{
|
||||
int res;
|
||||
xmlBufferPtr xml_buffer;
|
||||
xmlTextWriterPtr writer = NULL;
|
||||
|
||||
xmlInitParser();
|
||||
xml_buffer = xmlBufferCreate();
|
||||
if (!xml_buffer) {
|
||||
ERROR("cannot create xml buffer\n");
|
||||
xmlCleanupParser();
|
||||
return CYBERIADA_XML_ERROR;
|
||||
}
|
||||
writer = xmlNewTextWriterMemory(xml_buffer, 0);
|
||||
if (!writer) {
|
||||
ERROR("cannot create buffer writter\n");
|
||||
xmlBufferFree(xml_buffer);
|
||||
xmlCleanupParser();
|
||||
return CYBERIADA_XML_ERROR;
|
||||
}
|
||||
|
||||
res = cyberiada_process_encode_sm_document(doc, writer, format, flags);
|
||||
|
||||
if (res == CYBERIADA_NO_ERROR) {
|
||||
size_t size = xml_buffer->use;
|
||||
*buffer = (char*)malloc(size + 1);
|
||||
memcpy(*buffer, xml_buffer->content, size);
|
||||
(*buffer)[size] = 0;
|
||||
*buffer_size = size;
|
||||
} else {
|
||||
*buffer = NULL;
|
||||
*buffer_size = 0;
|
||||
}
|
||||
|
||||
xmlBufferFree(xml_buffer);
|
||||
xmlFreeTextWriter(writer);
|
||||
xmlCleanupParser();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
106
cyberiadaml.h
106
cyberiadaml.h
@@ -70,11 +70,11 @@ typedef enum {
|
||||
/* SM node & transitions geometry */
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
double x, y;
|
||||
} CyberiadaPoint;
|
||||
|
||||
typedef struct {
|
||||
float x, y, width, height;
|
||||
double x, y, width, height;
|
||||
} CyberiadaRect;
|
||||
|
||||
typedef struct _CyberiadaPolyline {
|
||||
@@ -108,14 +108,26 @@ typedef struct _CyberiadaLink {
|
||||
size_t ref_len;
|
||||
} CyberiadaLink;
|
||||
|
||||
/* SM node (state) */
|
||||
/* SM node (state)
|
||||
*
|
||||
* Comment on the node geometry:
|
||||
* - if the top-level node (state machine) has geometry, it is calculated in
|
||||
* absolute coordinates (x: left-to-right, y: top-to-bottom);
|
||||
* - if the state machine has no geometry, the first level nodes (comporite states, initial
|
||||
* vertexes, etc.) are calculated in absolute coordinates;
|
||||
* - the child nodes are calculated in local coordinates relative to the _center_ of the
|
||||
* parent node.
|
||||
*
|
||||
* NOTE: this geomerty is different from the Cyberiada GraphML 1.0 / YED formats geometry
|
||||
* and is being converted during the document import/export.
|
||||
*/
|
||||
typedef struct _CyberiadaNode {
|
||||
CyberiadaNodeType type;
|
||||
char* id;
|
||||
size_t id_len;
|
||||
char* title;
|
||||
size_t title_len;
|
||||
CyberiadaAction* actions; /* for simple & composite nodes */
|
||||
CyberiadaAction* actions; /* for simple & composite state nodes */
|
||||
CyberiadaCommentData* comment_data; /* for comments */
|
||||
CyberiadaLink* link; /* for submachine states */
|
||||
CyberiadaPoint* geometry_point; /* for some pseudostates & final state */
|
||||
@@ -139,7 +151,19 @@ typedef struct _CyberiadaCommentSubject {
|
||||
size_t fragment_len;
|
||||
} CyberiadaCommentSubject;
|
||||
|
||||
/* SM edge (transition) */
|
||||
/* SM edge (transition)
|
||||
*
|
||||
* Comment on the edge geometry:
|
||||
* - the source and target points are calculated in local coordinates relative
|
||||
* to the center of the corresponding source/target node;
|
||||
* - the label point is calculated in local coordinates relative to the geometrical
|
||||
* center of the edge (based on the bounding rectangle);
|
||||
* - the polyline points are calculated in local coordinated relative to the center of
|
||||
* the source node.
|
||||
*
|
||||
* NOTE: this geomerty is different from the Cyberiada GraphML 1.0 / YED formats geometry
|
||||
* and is being converted during the document import/export.
|
||||
*/
|
||||
typedef struct _CyberiadaEdge {
|
||||
CyberiadaEdgeType type;
|
||||
char* id;
|
||||
@@ -165,6 +189,7 @@ typedef struct _CyberiadaEdge {
|
||||
typedef struct _CyberiadaSM {
|
||||
CyberiadaNode* nodes;
|
||||
CyberiadaEdge* edges;
|
||||
CyberiadaRect* bounding_rect;
|
||||
struct _CyberiadaSM* next;
|
||||
} CyberiadaSM;
|
||||
|
||||
@@ -211,22 +236,52 @@ typedef struct {
|
||||
char* markup_language;
|
||||
size_t markup_language_len;
|
||||
} CyberiadaMetainformation;
|
||||
|
||||
|
||||
/* SM document node coordinates format */
|
||||
typedef enum {
|
||||
cybCoordNone = 0, /* no geometry information */
|
||||
cybCoordAbsolute = 1, /* absolute coordinates (used in YED) */
|
||||
cybCoordLeftTop = 2, /* left-top-oriented local coordinates (used in Cyberiada10) */
|
||||
cybCoordLocalCenter = 4, /* center-oriented local coordinates (used by default) */
|
||||
} CyberiadaGeometryCoordFormat;
|
||||
|
||||
/* SM document edges source/target point placement & coordinates format */
|
||||
typedef enum {
|
||||
cybEdgeNone = 0,
|
||||
cybEdgeLocalCenter = 1, /* source & target points are bind to a node's center */
|
||||
cybEdgeLeftTopBorder = 2, /* source & target points are placed on a node's border with left-top coordinates */
|
||||
cybEdgeCenterBorder = 4, /* source & target points are placed on a node's border with center coordinates */
|
||||
} CyberiadaGeometryEdgeFormat;
|
||||
|
||||
/* SM document */
|
||||
typedef struct {
|
||||
char* format; /* SM document format string */
|
||||
size_t format_len; /* SM document format string length */
|
||||
CyberiadaMetainformation* meta_info; /* SM document metainformation */
|
||||
CyberiadaSM* state_machines; /* State machines */
|
||||
char* format; /* SM document format string (additional info) */
|
||||
size_t format_len; /* SM document format string length */
|
||||
CyberiadaMetainformation* meta_info; /* SM document metainformation */
|
||||
CyberiadaGeometryCoordFormat geometry_format; /* SM document geometry coordinates format */
|
||||
CyberiadaGeometryEdgeFormat edge_geom_format; /* SM document edges geometry format */
|
||||
CyberiadaSM* state_machines; /* State machines */
|
||||
} CyberiadaDocument;
|
||||
|
||||
/* Cyberiada GraphML Library supported formats */
|
||||
typedef enum {
|
||||
cybxmlCyberiada10 = 0, /* Cyberiada 1.0 format */
|
||||
cybxmlYED = 1, /* Old YED-based Berloga/Ostranna format */
|
||||
cybxmlUnknown = 99 /* Format is not specified */
|
||||
cybxmlCyberiada10 = 0, /* Cyberiada 1.0 format */
|
||||
cybxmlYED = 1, /* Old YED-based Berloga/Ostranna format */
|
||||
cybxmlUnknown = 99 /* Format is not specified */
|
||||
} CyberiadaXMLFormat;
|
||||
|
||||
|
||||
/* Cyberiada GraphML Library import/export flags */
|
||||
#define CYBERIADA_FLAG_NO 0
|
||||
#define CYBERIADA_FLAG_ABSOLUTE_GEOMETRY 1 /* convert imported geometry to absolute coordinates */
|
||||
#define CYBERIADA_FLAG_LEFTTOP_LOCAL_GEOMETRY 2 /* convert imported geometry to left-top-oriented local coordinates */
|
||||
#define CYBERIADA_FLAG_CENTER_LOCAL_GEOMETRY 4 /* convert imported geometry to center-oriented local coordinates */
|
||||
#define CYBERIADA_FLAG_CENTER_EDGE_GEOMETRY 8 /* convert imported geometry to center edge coordinates */
|
||||
#define CYBERIADA_FLAG_LEFTTOP_BORDER_EDGE_GEOMETRY 16 /* convert imported geometry to border edge coordinates (left-top) */
|
||||
#define CYBERIADA_FLAG_CENTER_BORDER_EDGE_GEOMETRY 32 /* convert imported geometry to border edge coordinates (center) */
|
||||
#define CYBERIADA_FLAG_RECONSTRUCT_GEOMETRY 64 /* reconstruct absent node/edge geometry on import */
|
||||
#define CYBERIADA_FLAG_SKIP_GEOMETRY 128 /* skip geometry node/edge during import/export */
|
||||
#define CYBERIADA_FLAG_ROUND_GEOMETRY 256 /* export geometry with round coordinates to 0.001 */
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* The Cyberiada GraphML error codes
|
||||
* ----------------------------------------------------------------------------- */
|
||||
@@ -263,10 +318,19 @@ typedef enum {
|
||||
|
||||
/* Read an XML file and decode the SM structure */
|
||||
/* Allocate the SM document structure first */
|
||||
int cyberiada_read_sm_document(CyberiadaDocument* doc, const char* filename, CyberiadaXMLFormat format);
|
||||
int cyberiada_read_sm_document(CyberiadaDocument* doc, const char* filename, CyberiadaXMLFormat format, int flags);
|
||||
|
||||
/* Encode the SM document structure and write the data to an XML file */
|
||||
int cyberiada_write_sm_document(CyberiadaDocument* doc, const char* filename, CyberiadaXMLFormat format);
|
||||
int cyberiada_write_sm_document(CyberiadaDocument* doc, const char* filename, CyberiadaXMLFormat format, int flags);
|
||||
|
||||
/* Decode the SM structure */
|
||||
/* Allocate the SM document structure first */
|
||||
int cyberiada_decode_sm_document(CyberiadaDocument* doc, const char* buffer, size_t buffer_size,
|
||||
CyberiadaXMLFormat format, int flags);
|
||||
|
||||
/* Encode the SM document structure */
|
||||
int cyberiada_encode_sm_document(CyberiadaDocument* doc, char** buffer, size_t* buffer_size,
|
||||
CyberiadaXMLFormat format, int flags);
|
||||
|
||||
/* Print the SM structure to stdout */
|
||||
int cyberiada_print_sm_document(CyberiadaDocument* doc);
|
||||
@@ -301,7 +365,15 @@ typedef enum {
|
||||
/* Allocate and initialize the SM polyline structure in memory */
|
||||
CyberiadaPolyline* cyberiada_new_polyline(void);
|
||||
|
||||
/* Allocate and initialize the SM metainformation structure in memory */
|
||||
/* Free the SM polyline structure in memory */
|
||||
int cyberiada_destroy_polyline(CyberiadaPolyline* polyline);
|
||||
|
||||
/* Change the SM document geometry format and convert the SMs geometry data */
|
||||
int cyberiada_convert_document_geometry(CyberiadaDocument* doc,
|
||||
CyberiadaGeometryCoordFormat new_format,
|
||||
CyberiadaGeometryEdgeFormat new_edge_format);
|
||||
|
||||
/* Allocate and initialize the SM metainformation structure in memory */
|
||||
CyberiadaMetainformation* cyberiada_new_meta(void);
|
||||
|
||||
/* Initialize and copy string. Use this function to initialize strings in Cyberiada structures */
|
||||
|
||||
1209
geometry.c
Normal file
1209
geometry.c
Normal file
File diff suppressed because it is too large
Load Diff
59
geometry.h
Normal file
59
geometry.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
* The Cyberiada GraphML library implemention
|
||||
*
|
||||
* The geometry utilities
|
||||
*
|
||||
* 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 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_GEOMETRY_H
|
||||
#define __CYBERIADA_GEOMRTRY_H
|
||||
|
||||
#include "cyberiadaml.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* The Cyberiada GraphML geometry utilities
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
CyberiadaPoint* cyberiada_new_point(void);
|
||||
CyberiadaPoint* cyberiada_copy_point(CyberiadaPoint* src);
|
||||
|
||||
CyberiadaRect* cyberiada_new_rect(void);
|
||||
CyberiadaRect* cyberiada_copy_rect(CyberiadaRect* src);
|
||||
|
||||
CyberiadaPolyline* cyberiada_new_polyline(void);
|
||||
CyberiadaPolyline* cyberiada_copy_polyline(CyberiadaPolyline* src);
|
||||
int cyberiada_destroy_polyline(CyberiadaPolyline* polyline);
|
||||
|
||||
int cyberiada_clean_document_geometry(CyberiadaDocument* doc);
|
||||
int cyberiada_convert_document_geometry(CyberiadaDocument* doc,
|
||||
CyberiadaGeometryCoordFormat new_format,
|
||||
CyberiadaGeometryEdgeFormat new_edge_format);
|
||||
int cyberiada_import_document_geometry(CyberiadaDocument* doc,
|
||||
int flags, CyberiadaXMLFormat file_format);
|
||||
int cyberiada_export_document_geometry(CyberiadaDocument* doc,
|
||||
int flags, CyberiadaXMLFormat file_format);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user