diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9358432 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "c-style-checker"] + path = c-style-checker + url = https://git.shimatta.de/mhu/c-style-checker diff --git a/c-style-checker b/c-style-checker new file mode 160000 index 0000000..3a58e3d --- /dev/null +++ b/c-style-checker @@ -0,0 +1 @@ +Subproject commit 3a58e3dd1c2ef6de78df89c8fdc7ba96b51cd4e0 diff --git a/command-line.c b/command-line.c index 8f2d97d..e163d64 100644 --- a/command-line.c +++ b/command-line.c @@ -35,6 +35,7 @@ #include "cairo-output/cairo-output.h" #include "latex-output/latex-output.h" #include "external-renderer.h" +#include "gds-parser/gds-tree-checker.h" /** * @brief Delete layer_info and free nem element. @@ -69,8 +70,10 @@ void command_line_convert_gds(char *gds_name, char *pdf_name, char *tex_name, gb GList *layer_info_list = NULL; GList *cell_list; struct layer_info *linfo_temp; + struct gds_library *first_lib; struct gds_cell *toplevel_cell = NULL, *temp_cell; + /* Check if parameters are valid */ if (!gds_name || (!pdf_name && pdf) || (!tex_name && tex) || !layer_file || !cell_name) { printf("Probably missing argument. Check --help option\n"); @@ -81,14 +84,14 @@ void command_line_convert_gds(char *gds_name, char *pdf_name, char *tex_name, gb clear_lib_list(&libs); res = parse_gds_from_file(gds_name, &libs); if (res) - return; + goto ret_destroy_library_list; file = g_file_new_for_path(layer_file); stream = g_file_read(file, NULL, NULL); if (!stream) { printf("Layer mapping not readable!\n"); - goto destroy_file; + goto ret_destroy_file; } dstream = g_data_input_stream_new(G_INPUT_STREAM(stream)); i = 0; @@ -100,7 +103,7 @@ void command_line_convert_gds(char *gds_name, char *pdf_name, char *tex_name, gb linfo_temp = (struct layer_info *)malloc(sizeof(struct layer_info)); if (!linfo_temp) { printf("Out of memory\n"); - goto ret_clear_list; + goto ret_clear_layer_list; } linfo_temp->color.alpha = layer_color.alpha; linfo_temp->color.red = layer_color.red; @@ -116,9 +119,15 @@ void command_line_convert_gds(char *gds_name, char *pdf_name, char *tex_name, gb /* find_cell in first library. */ if (!libs) - goto ret_clear_list; + goto ret_clear_layer_list; - for (cell_list = ((struct gds_library *)libs->data)->cells; cell_list != NULL; cell_list = g_list_next(cell_list)) { + first_lib = (struct gds_library *)libs->data; + if (!first_lib) { + fprintf(stderr, "No library in library list. This should not happen.\n"); + goto ret_clear_layer_list; + } + + for (cell_list = first_lib->cells; cell_list != NULL; cell_list = g_list_next(cell_list)) { temp_cell = (struct gds_cell *)cell_list->data; if (!strcmp(temp_cell->name, cell_name)) { toplevel_cell = temp_cell; @@ -128,9 +137,31 @@ void command_line_convert_gds(char *gds_name, char *pdf_name, char *tex_name, gb if (!toplevel_cell) { printf("Couldn't find cell in first library!\n"); - goto ret_clear_list; + goto ret_clear_layer_list; } + /* Check if cell passes vital checks */ + res = gds_tree_check_reference_loops(toplevel_cell->parent_library); + if (res < 0) { + fprintf(stderr, "Checking library %s failed.\n", first_lib->name); + goto ret_clear_layer_list; + } else if (res > 0) { + fprintf(stderr, "%d reference loops found.\n", res); + + /* do further checking if the specified cell and/or its subcells are affected */ + if (toplevel_cell->checks.affected_by_reference_loop == 1) { + fprintf(stderr, "Cell is affected by reference loop. Abort!\n"); + goto ret_clear_layer_list; + } + } + + if (toplevel_cell->checks.affected_by_reference_loop == GDS_CELL_CHECK_NOT_RUN) + fprintf(stderr, "Cell was not checked. This should not happen. Please report this issue. Will continue either way.\n"); + + /* Note: unresolved references are not an abort condition. + * Deal with it. + */ + /* Render outputs */ if (pdf == TRUE || svg == TRUE) { cairo_render_cell_to_vector_file(toplevel_cell, layer_info_list, (pdf == TRUE ? pdf_name : NULL), @@ -140,14 +171,14 @@ void command_line_convert_gds(char *gds_name, char *pdf_name, char *tex_name, gb if (tex == TRUE) { tex_file = fopen(tex_name, "w"); if (!tex_file) - goto ret_clear_list; + goto ret_clear_layer_list; latex_render_cell_to_code(toplevel_cell, layer_info_list, tex_file, scale, pdf_layers, pdf_standalone); fclose(tex_file); } if (so_name && so_out_file) { if (strlen(so_name) == 0 || strlen(so_out_file) == 0) - goto ret_clear_list; + goto ret_clear_layer_list; /* Render output using external renderer */ printf("Invoking external renderer!\n"); @@ -155,15 +186,16 @@ void command_line_convert_gds(char *gds_name, char *pdf_name, char *tex_name, gb printf("External renderer finished!\n"); } -ret_clear_list: +ret_clear_layer_list: g_list_free_full(layer_info_list, (GDestroyNotify)delete_layer_info_with_name); g_object_unref(dstream); g_object_unref(stream); -destroy_file: +ret_destroy_file: g_object_unref(file); - - + /* Delete all allocated libraries */ +ret_destroy_library_list: + clear_lib_list(&libs); } /** @} */ diff --git a/doxygen/trigonometric.dox b/doxygen/trigonometric.dox index 52819eb..3fc3863 100644 --- a/doxygen/trigonometric.dox +++ b/doxygen/trigonometric.dox @@ -2,7 +2,7 @@ /** * @defgroup trigonometric Trigonometric Helper Functions - * @description The trigonometric helper function will be used in future releases to pcalculate bounding boxes - * @note Currently not in use! - * @warning Code is incomplete. DO NOT USE! + * + * The trigonometric helper function are used to calculate bounding boxes + * @warning Code is incomplete. Please double check for functionality! */ diff --git a/doxygen/usage.dox b/doxygen/usage.dox index b46fb87..254ee82 100644 --- a/doxygen/usage.dox +++ b/doxygen/usage.dox @@ -6,7 +6,7 @@ To use the application on the command line check 'gds-render --help'. Application Options: - -t, --tikz Output TikZ code - -p, --pdf Output PDF document -- -s, --scale= Divide output coordinates by +- -s, --scale=SCALE Divide output coordinates by SCALE - -o, --tex-output=PATH Optional path for TeX file - -O, --pdf-output=PATH Optional path for PDF file - -m, --mapping=PATH Path for Layer Mapping File diff --git a/external-renderer.c b/external-renderer.c index 8b7dd90..5474b16 100644 --- a/external-renderer.c +++ b/external-renderer.c @@ -32,7 +32,8 @@ #include #include -int external_renderer_render_cell(struct gds_cell *toplevel_cell, GList *layer_info_list, char *output_file, char *so_path) +int external_renderer_render_cell(struct gds_cell *toplevel_cell, GList *layer_info_list, + char *output_file, char *so_path) { int (*so_render_func)(struct gds_cell *, GList *, char *) = NULL; void *so_handle = NULL; @@ -40,9 +41,8 @@ int external_renderer_render_cell(struct gds_cell *toplevel_cell, GList *layer_i int ret = 0; /* Check parameter sanity */ - if (!output_file || !so_path || !toplevel_cell || !layer_info_list) { + if (!output_file || !so_path || !toplevel_cell || !layer_info_list) return -3000; - } /* Load shared object */ so_handle = dlopen(so_path, RTLD_LAZY); @@ -53,7 +53,8 @@ int external_renderer_render_cell(struct gds_cell *toplevel_cell, GList *layer_i /* Load symbol from library */ so_render_func = (int (*)(struct gds_cell *, GList *, char *))dlsym(so_handle, EXTERNAL_LIBRARY_FUNCTION); - if ((error_msg = dlerror()) != NULL) { + error_msg = dlerror(); + if (error_msg != NULL) { printf("Rendering function not found in library:\n%s\n", error_msg); goto ret_close_so_handle; } diff --git a/external-renderer.h b/external-renderer.h index ecb056b..841b219 100644 --- a/external-renderer.h +++ b/external-renderer.h @@ -36,7 +36,8 @@ /** * @brief function name expected to be found in external library. - * @detail The function has to be defined as follows: + * + * The function has to be defined as follows: * @code * int function_name(gds_cell *toplevel, GList *layer_info_list, char *output_file_name) * @endcode diff --git a/gds-parser/gds-parser.c b/gds-parser/gds-parser.c index e6ef61b..185c6b4 100644 --- a/gds-parser/gds-parser.c +++ b/gds-parser/gds-parser.c @@ -17,11 +17,6 @@ * along with GDSII-Converter. If not, see . */ -/* - - */ - - /** * @file gds-parser.c * @brief Implementation of the GDS-Parser @@ -35,7 +30,7 @@ */ /** - * @addtogroup GDS-Parser + * @addtogroup GDS-Utilities * @{ */ @@ -304,6 +299,8 @@ static GList *append_cell(GList *curr_list, struct gds_cell **cell_ptr) cell->graphic_objs = NULL; cell->name[0] = 0; cell->parent_library = NULL; + cell->checks.unresolved_child_count = GDS_CELL_CHECK_NOT_RUN; + cell->checks.affected_by_reference_loop = GDS_CELL_CHECK_NOT_RUN; } else return NULL; /* return cell */ @@ -665,7 +662,7 @@ int parse_gds_from_file(const char *filename, GList **library_list) break; case SREF: if (current_cell == NULL) { - GDS_ERROR("Path outside of cell"); + GDS_ERROR("Cell Reference outside of cell"); run = -3; break; } @@ -813,7 +810,11 @@ int parse_gds_from_file(const char *filename, GList **library_list) break; case SNAME: - name_cell_ref(current_s_reference, read, workbuff); + if (current_s_reference) { + name_cell_ref(current_s_reference, (unsigned int)read, workbuff); + } else { + GDS_ERROR("reference name set outside of cell reference.\n"); + } break; case WIDTH: if (!current_graphics) { @@ -866,7 +867,7 @@ int parse_gds_from_file(const char *filename, GList **library_list) break; } if (current_graphics->gfx_type == GRAPHIC_PATH) { - current_graphics->path_render_type = (int)gds_convert_signed_int16(workbuff); + current_graphics->path_render_type = (enum path_type)gds_convert_signed_int16(workbuff); GDS_INF("\t\tPathtype: %d\n", current_graphics->path_render_type); } else { GDS_WARN("Path type defined inside non-path graphics object. Ignoring"); @@ -897,7 +898,8 @@ int parse_gds_from_file(const char *filename, GList **library_list) */ static void delete_cell_inst_element(struct gds_cell_instance *cell_inst) { - free(cell_inst); + if (cell_inst) + free(cell_inst); } /** @@ -906,7 +908,8 @@ static void delete_cell_inst_element(struct gds_cell_instance *cell_inst) */ static void delete_vertex(struct gds_point *vertex) { - free(vertex); + if (vertex) + free(vertex); } /** @@ -915,6 +918,9 @@ static void delete_vertex(struct gds_point *vertex) */ static void delete_graphics_obj(struct gds_graphics *gfx) { + if (!gfx) + return; + g_list_free_full(gfx->vertices, (GDestroyNotify)delete_vertex); free(gfx); } @@ -925,6 +931,9 @@ static void delete_graphics_obj(struct gds_graphics *gfx) */ static void delete_cell_element(struct gds_cell *cell) { + if (!cell) + return; + g_list_free_full(cell->child_cells, (GDestroyNotify)delete_cell_inst_element); g_list_free_full(cell->graphic_objs, (GDestroyNotify)delete_graphics_obj); free(cell); @@ -936,6 +945,9 @@ static void delete_cell_element(struct gds_cell *cell) */ static void delete_library_element(struct gds_library *lib) { + if (!lib) + return; + g_list_free(lib->cell_names); g_list_free_full(lib->cells, (GDestroyNotify)delete_cell_element); free(lib); @@ -943,8 +955,12 @@ static void delete_library_element(struct gds_library *lib) int clear_lib_list(GList **library_list) { + if (!library_list) + return 0; + if (*library_list == NULL) return 0; + g_list_free_full(*library_list, (GDestroyNotify)delete_library_element); *library_list = NULL; return 0; diff --git a/gds-parser/gds-parser.h b/gds-parser/gds-parser.h index 1704395..ce745e4 100644 --- a/gds-parser/gds-parser.h +++ b/gds-parser/gds-parser.h @@ -24,7 +24,7 @@ */ /** - * @addtogroup GDS-Parser + * @addtogroup GDS-Utilities * @{ */ diff --git a/gds-parser/gds-tree-checker.c b/gds-parser/gds-tree-checker.c new file mode 100644 index 0000000..3d067fb --- /dev/null +++ b/gds-parser/gds-tree-checker.c @@ -0,0 +1,200 @@ +/* + * GDSII-Converter + * Copyright (C) 2019 Mario Hüttel + * + * This file is part of GDSII-Converter. + * + * GDSII-Converter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter 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 GDSII-Converter. If not, see . + */ + +/** + * @file gds-tree-checker.c + * @brief Checking functions of a cell tree + * + * This file contains cehcking functions for the GDS cell tree. + * These functions include checks if all child references could be resolved, + * and if the cell tree contains loops. + * + * @author Mario Hüttel + */ + +/** + * @addtogroup GDS-Utilities + * @{ + */ + +#include "gds-tree-checker.h" +#include + +int gds_tree_check_cell_references(struct gds_library *lib) +{ + GList *cell_iter; + struct gds_cell *cell; + GList *instance_iter; + struct gds_cell_instance *cell_inst; + int total_unresolved_count = 0; + + if (!lib) + return -1; + + /* Iterate over all cells in library */ + for (cell_iter = lib->cells; cell_iter != NULL; cell_iter = g_list_next(cell_iter)) { + cell = (struct gds_cell *)cell_iter->data; + + /* Check if this list element is broken. This should never happen */ + if (!cell) { + fprintf(stderr, "Broken cell list item found. Will continue.\n"); + continue; + } + + /* Reset the unresolved cell reference counter to 0 */ + cell->checks.unresolved_child_count = 0; + + /* Iterate through all child cell references and check if the references are set */ + for (instance_iter = cell->child_cells; instance_iter != NULL; + instance_iter = g_list_next(instance_iter)) { + cell_inst = (struct gds_cell_instance *)instance_iter->data; + + /* Check if broken. This should not happen */ + if (!cell_inst) { + fprintf(stderr, "Broken cell list item found in cell %s. Will continue.\n", + cell->name); + continue; + } + + /* Check if instance is valid; else increment "error" counter of cell */ + if (!cell_inst->cell_ref) { + total_unresolved_count++; + cell->checks.unresolved_child_count++; + } + } + } + + return total_unresolved_count; +} + +/** + * @brief Check if list contains a cell + * @param list GList to check. May be a null pointer + * @param cell Cell to check for + * @return 0 if cell is not in list. 1 if cell is in list + */ +static int gds_tree_check_list_contains_cell(GList *list, struct gds_cell *cell) { + GList *iter; + + for (iter = list; iter != NULL; iter = g_list_next(iter)) { + if ((struct gds_cell *)iter->data == cell) + return 1; + } + + return 0; +} + +/** + * @brief This function follows down the reference list of a cell and marks each visited subcell and detects loops + * @param cell_to_check The cell to check for reference loops + * @param visited_cells Pointer to list head. May be zero. + * @return 0 if no loops exist; error in processing: <0; loop found: >0 + */ +static int gds_tree_check_iterate_ref_and_check(struct gds_cell *cell_to_check, GList **visited_cells) +{ + GList *ref_iter; + struct gds_cell_instance *ref; + struct gds_cell *sub_cell; + int res; + + if (!cell_to_check) + return -1; + + /* Check if this cell is already contained in visited cells. This indicates a loop */ + if (gds_tree_check_list_contains_cell(*visited_cells, cell_to_check)) + return 1; + + /* Add cell to visited cell list */ + *visited_cells = g_list_append(*visited_cells, (gpointer)cell_to_check); + + /* Mark references and process sub cells */ + for (ref_iter = cell_to_check->child_cells; ref_iter != NULL; ref_iter = g_list_next(ref_iter)) { + ref = (struct gds_cell_instance *)ref_iter->data; + + if (!ref) + return -1; + + sub_cell = ref->cell_ref; + + /* If cell is not resolved, ignore. No harm there */ + if (!sub_cell) + continue; + + res = gds_tree_check_iterate_ref_and_check(sub_cell, visited_cells); + if (res < 0) { + /* Error. return. */ + return -3; + } else if (res > 0) { + /* Loop in subcell found. Propagate to top */ + return 1; + } + } + + /* Remove cell from visted cells */ + *visited_cells = g_list_remove(*visited_cells, cell_to_check); + + /* No error found in this chain */ + return 0; +} + +int gds_tree_check_reference_loops(struct gds_library *lib) +{ + int res; + int loop_count = 0; + GList *cell_iter; + struct gds_cell *cell_to_check; + GList *visited_cells = NULL; + + + if (!lib) + return -1; + + for (cell_iter = lib->cells; cell_iter != NULL; cell_iter = g_list_next(cell_iter)) { + cell_to_check = (struct gds_cell *)cell_iter->data; + + /* A broken cell reference will be counted fatal in this case */ + if (!cell_to_check) + return -2; + + /* iterate through references and check if loop exists */ + res = gds_tree_check_iterate_ref_and_check(cell_to_check, &visited_cells); + + if (res < 0) { + /* Error */ + return res; + } else if (res > 0) { + /* Loop found: increment loop count and flag cell */ + cell_to_check->checks.affected_by_reference_loop = 1; + loop_count++; + } else if (res == 0) { + /* No error found for this cell */ + cell_to_check->checks.affected_by_reference_loop = 0; + } + + } + + if (visited_cells) { + fprintf(stderr, "Visited cell list should be empty. This is a bug. Please report this.\n"); + g_list_free(visited_cells); + } + + return loop_count; +} + +/** @} */ diff --git a/gds-parser/gds-tree-checker.h b/gds-parser/gds-tree-checker.h new file mode 100644 index 0000000..199ace5 --- /dev/null +++ b/gds-parser/gds-tree-checker.h @@ -0,0 +1,61 @@ +/* + * GDSII-Converter + * Copyright (C) 2019 Mario Hüttel + * + * This file is part of GDSII-Converter. + * + * GDSII-Converter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter 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 GDSII-Converter. If not, see . + */ + +/** + * @file gds-tree-checker.h + * @brief Checking functions of a cell tree (Header) + * @author Mario Hüttel + */ + +/** + * @addtogroup GDS-Utilities + * @{ + */ + +#ifndef _GDS_TREE_CHECKER_H_ +#define _GDS_TREE_CHECKER_H_ + +#include "gds-types.h" + +/** + * @brief gds_tree_check_cell_references checks if all child cell references can be resolved in the given library + * + * This function will only mark cells that + * directly contain unresolved references. + * + * If a cell contains a reference to a cell with unresolved references, it is not flagged. + * + * @param lib The GDS library to check + * @return less than 0 if an error occured during processing; 0 if all child cells could be resolved; + * greater than zero if the processing was successful but not all cell references could be resolved. + * In this case the number of unresolved references is returned + */ +int gds_tree_check_cell_references(struct gds_library *lib); + +/** + * @brief gds_tree_check_reference_loops checks if the given library contains reference loops + * @param lib GDS library + * @return negative if an error occured, zero if there are no reference loops, else a positive number representing the number + * of affected cells + */ +int gds_tree_check_reference_loops(struct gds_library *lib); + +#endif /* _GDS_TREE_CHECKER_H_ */ + +/** @} */ diff --git a/gds-parser/gds-types.h b/gds-parser/gds-types.h index 80e2acc..1be507c 100644 --- a/gds-parser/gds-types.h +++ b/gds-parser/gds-types.h @@ -24,7 +24,7 @@ */ /** - * @addtogroup GDS-Parser + * @addtogroup GDS-Utilities * @{ */ @@ -35,9 +35,15 @@ #include #define CELL_NAME_MAX (100) /**< @brief Maximum length of a gds_cell::name or a gds_library::name */ + +/* Maybe use the macros that ship with the compiler? */ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) /**< @brief Return smaller number */ #define MAX(a,b) (((a) > (b)) ? (a) : (b)) /**< @brief Return bigger number */ +/** @brief Defintion of check counter default value + * that indicates that the corresponding check has not yet been executed */ +enum {GDS_CELL_CHECK_NOT_RUN = -1}; + /** @brief Types of graphic objects */ enum graphics_type { @@ -59,6 +65,21 @@ struct gds_point { int y; }; +/** + * @brief Stores the result of the cell checks. + */ +struct gds_cell_checks { + int unresolved_child_count; /**< @brief Number of unresolved cell instances inside this cell. Default: GDS_CELL_CHECK_NOT_RUN */ + int affected_by_reference_loop; /**< @brief 1 if the cell is affected by a reference loop and therefore not renderable. Default: GDS_CELL_CHECK_NOT_RUN*/ + /** + * @brief For the internal use of the checker. + * @warning Do not use this structure and its contents! + */ + struct _check_internals { + int marker; + } _internal; +}; + /** * @brief Date information for cells and libraries */ @@ -105,6 +126,7 @@ struct gds_cell { GList *child_cells; /**< @brief List of #gds_cell_instance elements */ GList *graphic_objs; /**< @brief List of #gds_graphics */ struct gds_library *parent_library; /**< @brief Pointer to parent library */ + struct gds_cell_checks checks; /**< @brief Checking results */ }; /** diff --git a/main-window.c b/main-window.c index 3475461..edd3d58 100644 --- a/main-window.c +++ b/main-window.c @@ -38,6 +38,8 @@ #include "cairo-output/cairo-output.h" #include "trigonometric/cell-trigonometrics.h" #include "version/version.h" +#include "tree-renderer/lib-cell-renderer.h" +#include "gds-parser/gds-tree-checker.h" /** * @brief User data supplied to callback function of the open button @@ -115,10 +117,13 @@ static void on_load_gds(gpointer button, gpointer user) char *filename; GString *mod_date; GString *acc_date; - GdkRGBA cell_text_color; + unsigned int cell_error_level; - open_dialog = gtk_file_chooser_dialog_new("Open GDSII File", ptr->main_window, GTK_FILE_CHOOSER_ACTION_OPEN, - "Cancel", GTK_RESPONSE_CANCEL, "Open GDSII", GTK_RESPONSE_ACCEPT, NULL); + open_dialog = gtk_file_chooser_dialog_new("Open GDSII File", ptr->main_window, + GTK_FILE_CHOOSER_ACTION_OPEN, + "Cancel", GTK_RESPONSE_CANCEL, + "Open GDSII", GTK_RESPONSE_ACCEPT, + NULL); file_chooser = GTK_FILE_CHOOSER(open_dialog); /* Add GDS II Filter */ filter = gtk_file_filter_new(); @@ -151,17 +156,21 @@ static void on_load_gds(gpointer button, gpointer user) for (lib = *(ptr->list_ptr); lib != NULL; lib = lib->next) { gds_lib = (struct gds_library *)lib->data; /* Create top level iter */ - gtk_tree_store_append (store, &libiter, NULL); + gtk_tree_store_append(store, &libiter, NULL); /* Convert dates to String */ mod_date = generate_string_from_date(&gds_lib->mod_time); acc_date = generate_string_from_date(&gds_lib->access_time); - gtk_tree_store_set (store, &libiter, - CELL_SEL_LIBRARY, gds_lib, - CELL_SEL_MODDATE, mod_date->str, - CELL_SEL_ACCESSDATE, acc_date->str, - -1); + gtk_tree_store_set(store, &libiter, + CELL_SEL_LIBRARY, gds_lib, + CELL_SEL_MODDATE, mod_date->str, + CELL_SEL_ACCESSDATE, acc_date->str, + -1); + + /* Check this library. This might take a while */ + (void)gds_tree_check_cell_references(gds_lib); + (void)gds_tree_check_reference_loops(gds_lib); /* Delete GStrings including string data. */ /* Cell store copies String type data items */ @@ -170,27 +179,28 @@ static void on_load_gds(gpointer button, gpointer user) for (cell = gds_lib->cells; cell != NULL; cell = cell->next) { gds_c = (struct gds_cell *)cell->data; - gtk_tree_store_append (store, &celliter, &libiter); + gtk_tree_store_append(store, &celliter, &libiter); /* Convert dates to String */ mod_date = generate_string_from_date(&gds_c->mod_time); acc_date = generate_string_from_date(&gds_c->access_time); - cell_text_color.alpha = 1; - cell_text_color.red = (double)61.0/(double)255.0; - cell_text_color.green = (double)152.0/(double)255.0; - cell_text_color.blue = 0.0; + /* Get the checking results for this cell */ + cell_error_level = 0; + if (gds_c->checks.unresolved_child_count) + cell_error_level |= LIB_CELL_RENDERER_ERROR_WARN; - /* Add cell to tree store model - * CELL_SEL_CELL_COLOR will always be green, - * because no cell cehcker is implemented, yet. - */ - gtk_tree_store_set (store, &celliter, - CELL_SEL_CELL, gds_c, - CELL_SEL_MODDATE, mod_date->str, - CELL_SEL_ACCESSDATE, acc_date->str, - CELL_SEL_CELL_COLOR, &cell_text_color, // TODO: implement cell checker - -1); + /* Check if it is completely b0rken */ + if (gds_c->checks.affected_by_reference_loop) + cell_error_level |= LIB_CELL_RENDERER_ERROR_ERR; + + /* Add cell to tree store model */ + gtk_tree_store_set(store, &celliter, + CELL_SEL_CELL, gds_c, + CELL_SEL_MODDATE, mod_date->str, + CELL_SEL_ACCESSDATE, acc_date->str, + CELL_SEL_CELL_ERROR_STATE, cell_error_level, + -1); /* Delete GStrings including string data. */ /* Cell store copies String type data items */ @@ -232,7 +242,7 @@ static void on_convert_clicked(gpointer button, gpointer user) gint res; char *file_name; union bounding_box cell_box; - double height, width; + unsigned int height, width; /* Get selected cell */ selection = gtk_tree_view_get_selection(data->tree_view); @@ -251,9 +261,12 @@ static void on_convert_clicked(gpointer button, gpointer user) bounding_box_prepare_empty(&cell_box); calculate_cell_bounding_box(&cell_box, cell_to_render); - /* Calculate size in meters database units */ - height = (cell_box.vectors.upper_right.y - cell_box.vectors.lower_left.y); - width = (cell_box.vectors.upper_right.x - cell_box.vectors.lower_left.x); + /* Calculate size in database units + * Note that the results are bound to be positive, + * so casting them to unsigned int is asbsolutely valid + */ + height = (unsigned int)(cell_box.vectors.upper_right.y - cell_box.vectors.lower_left.y); + width = (unsigned int)(cell_box.vectors.upper_right.x - cell_box.vectors.lower_left.x); /* Show settings dialog */ settings = renderer_settings_dialog_new(GTK_WINDOW(data->main_window)); @@ -312,8 +325,12 @@ static void on_convert_clicked(gpointer button, gpointer user) case RENDERER_CAIROGRAPHICS_SVG: case RENDERER_CAIROGRAPHICS_PDF: cairo_render_cell_to_vector_file(cell_to_render, layer_list, - (sett.renderer == RENDERER_CAIROGRAPHICS_PDF ? file_name : NULL), - (sett.renderer == RENDERER_CAIROGRAPHICS_SVG ? file_name : NULL), + (sett.renderer == RENDERER_CAIROGRAPHICS_PDF + ? file_name + : NULL), + (sett.renderer == RENDERER_CAIROGRAPHICS_SVG + ? file_name + : NULL), sett.scale); break; } @@ -405,7 +422,7 @@ GtkWindow *create_main_window() g_object_unref(main_builder); - return (conv_data.main_window); + return conv_data.main_window; } /** @} */ diff --git a/main.c b/main.c index 89274fd..2657ad5 100644 --- a/main.c +++ b/main.c @@ -32,7 +32,7 @@ struct application_data { static void app_quit(GSimpleAction *action, GVariant *parameter, gpointer user_data) { - struct application_data *appdata = (struct application_data *)user_data; + const struct application_data * const appdata = (const struct application_data *)user_data; (void)action; (void)parameter; @@ -43,7 +43,7 @@ static void app_about(GSimpleAction *action, GVariant *parameter, gpointer user_ { GtkBuilder *builder; GtkDialog *dialog; - struct application_data *appdata = (struct application_data *)user_data; + const struct application_data * const appdata = (const struct application_data *)user_data; (void)action; (void)parameter; @@ -64,7 +64,7 @@ const static GActionEntry app_actions[] = { static void gapp_activate(GApplication *app, gpointer user_data) { GtkWindow *main_window; - struct application_data *appdata = (struct application_data *)user_data; + struct application_data * const appdata = (struct application_data *)user_data; main_window = create_main_window(); appdata->main_window = main_window; @@ -142,7 +142,7 @@ int main(int argc, char **argv) {"tex-layers", 'l', 0, G_OPTION_ARG_NONE, &pdf_layers, "Create PDF Layers (OCG)", NULL }, {"custom-render-lib", 'P', 0, G_OPTION_ARG_FILENAME, &custom_library_path, "Path to a custom shared object, that implements the " EXTERNAL_LIBRARY_FUNCTION " function", "PATH"}, {"external-lib-output", 'e', 0, G_OPTION_ARG_FILENAME, &custom_library_file_name, "Output path for external render library", "PATH"}, - { NULL } + {NULL} }; context = g_option_context_new(" FILE - Convert GDS file to graphic"); diff --git a/tree-renderer/lib-cell-renderer.c b/tree-renderer/lib-cell-renderer.c index 192210b..99e96e5 100644 --- a/tree-renderer/lib-cell-renderer.c +++ b/tree-renderer/lib-cell-renderer.c @@ -25,6 +25,7 @@ G_DEFINE_TYPE(LibCellRenderer, lib_cell_renderer, GTK_TYPE_CELL_RENDERER_TEXT) enum { PROP_LIB = 1, PROP_CELL, + PROP_ERROR_LEVEL, PROP_COUNT }; @@ -38,24 +39,56 @@ static void lib_cell_renderer_constructed(GObject *obj) G_OBJECT_CLASS(lib_cell_renderer_parent_class)->constructed(obj); } +static void convert_error_level_to_color(GdkRGBA *color, unsigned int error_level) +{ + + /* Always use no transparency */ + color->alpha = 1.0; + + if (error_level & LIB_CELL_RENDERER_ERROR_ERR) { + /* Error set. Color cell red */ + color->red = 1.0; + color->blue = 0.0; + color->green = 0.0; + } else if (error_level & LIB_CELL_RENDERER_ERROR_WARN) { + /* Only warning set; orange color */ + color->red = 1.0; + color->blue = 0.0; + color->green = 0.6; + } else { + /* Everything okay; green color */ + color->red = (double)61.0/(double)255.0; + color->green = (double)152.0/(double)255.0; + color->blue = 0.0; + } +} + static void lib_cell_renderer_set_property(GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { GValue val = G_VALUE_INIT; - - g_value_init(&val, G_TYPE_STRING); + GdkRGBA color; switch (param_id) { case PROP_LIB: + g_value_init(&val, G_TYPE_STRING); g_value_set_string(&val, ((struct gds_library *)g_value_get_pointer(value))->name); g_object_set_property(object, "text", &val); break; case PROP_CELL: + g_value_init(&val, G_TYPE_STRING); g_value_set_string(&val, ((struct gds_cell *)g_value_get_pointer(value))->name); g_object_set_property(object, "text", &val); break; + case PROP_ERROR_LEVEL: + /* Set cell color according to error level */ + g_value_init(&val, GDK_TYPE_RGBA); + convert_error_level_to_color(&color, g_value_get_uint(value)); + g_value_set_boxed(&val, &color); + g_object_set_property(object, "foreground-rgba", &val); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); break; @@ -89,6 +122,8 @@ void lib_cell_renderer_class_init(LibCellRendererClass *klass) properties[PROP_CELL] = g_param_spec_pointer("gds-cell", "gds-cell", "Cell reference to be displayed", G_PARAM_WRITABLE); + properties[PROP_ERROR_LEVEL] = g_param_spec_uint("error-level", "error-level", + "Error level of this cell", 0, 255, 0, G_PARAM_WRITABLE); g_object_class_install_properties(oclass, PROP_COUNT, properties); } diff --git a/tree-renderer/lib-cell-renderer.h b/tree-renderer/lib-cell-renderer.h index 4e0949f..b71888a 100644 --- a/tree-renderer/lib-cell-renderer.h +++ b/tree-renderer/lib-cell-renderer.h @@ -27,6 +27,9 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(LibCellRenderer, lib_cell_renderer, LIB_CELL, RENDERER, GtkCellRendererText) #define TYPE_LIB_CELL_RENDERER (lib_cell_renderer_get_type()) +#define LIB_CELL_RENDERER_ERROR_WARN (1U<<0) +#define LIB_CELL_RENDERER_ERROR_ERR (1U<<1) + typedef struct _LibCellRenderer { /* Inheritance */ GtkCellRendererText super; diff --git a/tree-renderer/tree-store.c b/tree-renderer/tree-store.c index 2ba5c11..3da3d63 100644 --- a/tree-renderer/tree-store.c +++ b/tree-renderer/tree-store.c @@ -49,15 +49,20 @@ static gboolean tree_sel_func(GtkTreeSelection *selection, { GtkTreeIter iter; struct gds_cell *cell; + unsigned int error_level; + gboolean ret = FALSE; gtk_tree_model_get_iter(model, &iter, path); - gtk_tree_model_get(model, &iter, CELL_SEL_CELL, &cell, -1); + gtk_tree_model_get(model, &iter, CELL_SEL_CELL, &cell, CELL_SEL_CELL_ERROR_STATE, &error_level, -1); - /* Allow only rows with valid cell to be selected */ - if (cell) - return TRUE; - else - return FALSE; + /* Allow only rows with _valid_ cell to be selected */ + if (cell) { + /* Cell available. Check if it passed the critical checks */ + if (!(error_level & LIB_CELL_RENDERER_ERROR_ERR)) + ret = TRUE; + } + + return ret; } /** @@ -127,7 +132,7 @@ struct tree_stores *setup_cell_selector(GtkTreeView* view, GtkEntry *search_entr stores.base_tree_view = view; stores.search_entry = search_entry; - stores.base_store = gtk_tree_store_new(CELL_SEL_COLUMN_COUNT, G_TYPE_POINTER, G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_STRING, G_TYPE_STRING); + stores.base_store = gtk_tree_store_new(CELL_SEL_COLUMN_COUNT, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); /* Searching */ if (search_entry) { @@ -147,8 +152,8 @@ struct tree_stores *setup_cell_selector(GtkTreeView* view, GtkEntry *search_entr column = gtk_tree_view_column_new_with_attributes("Library", render_lib, "gds-lib", CELL_SEL_LIBRARY, NULL); gtk_tree_view_append_column(view, column); - /* Cell color: #3D9801 */ - column = gtk_tree_view_column_new_with_attributes("Cell", render_cell, "gds-cell", CELL_SEL_CELL, "foreground-rgba", CELL_SEL_CELL_COLOR, NULL); + column = gtk_tree_view_column_new_with_attributes("Cell", render_cell, "gds-cell", CELL_SEL_CELL, + "error-level", CELL_SEL_CELL_ERROR_STATE, NULL); gtk_tree_view_append_column(view, column); column = gtk_tree_view_column_new_with_attributes("Mod. Date", render_dates, "text", CELL_SEL_MODDATE, NULL); diff --git a/tree-renderer/tree-store.h b/tree-renderer/tree-store.h index aa6d283..02a2ee7 100644 --- a/tree-renderer/tree-store.h +++ b/tree-renderer/tree-store.h @@ -37,10 +37,10 @@ enum cell_store_columns { CELL_SEL_LIBRARY = 0, CELL_SEL_CELL, - CELL_SEL_CELL_COLOR, /**< Cell column color */ + CELL_SEL_CELL_ERROR_STATE, /**< Used for cell color and selectability */ CELL_SEL_MODDATE, CELL_SEL_ACCESSDATE, - CELL_SEL_COLUMN_COUNT /**< Not a column. Used to determine count of coumns **/ + CELL_SEL_COLUMN_COUNT /**< @brief Not a column. Used to determine count of columns */ }; struct tree_stores { diff --git a/trigonometric/bounding-box.c b/trigonometric/bounding-box.c index 9d3b26d..c30541d 100644 --- a/trigonometric/bounding-box.c +++ b/trigonometric/bounding-box.c @@ -180,10 +180,11 @@ void bounding_box_update_point(union bounding_box *destination, conv_generic_to_ } /** - * @brief bounding_box_apply_transform - * @param scale scaling factor - * @param rotation roation of bounding box around the origin in degrees (counterclockwise) - * @param box bounding box the operations should be applied to + * @brief Apply transformations onto bounding box. + * @param scale Scaling factor + * @param rotation_deg Roation of bounding box around the origin in degrees (counterclockwise) + * @param flip_at_x Flip the boundig box on the x axis before rotating. + * @param box Bounding box the operations should be applied to. */ void bounding_box_apply_transform(double scale, double rotation_deg, bool flip_at_x, union bounding_box *box) { diff --git a/trigonometric/cell-trigonometrics.c b/trigonometric/cell-trigonometrics.c index 823a531..4789a46 100644 --- a/trigonometric/cell-trigonometrics.c +++ b/trigonometric/cell-trigonometrics.c @@ -38,9 +38,9 @@ static void convert_gds_point_to_2d_vector(struct gds_point *pt, struct vector_2 } /** - * @brief update_box_with_gfx - * @param box - * @param gfx_list + * @brief Update the given bounding box with the bounding box of a graphics element. + * @param box box to update + * @param gfx Graphics element */ static void update_box_with_gfx(union bounding_box *box, struct gds_graphics *gfx) { @@ -94,14 +94,16 @@ void calculate_cell_bounding_box(union bounding_box *box, struct gds_cell *cell) } /* Update bounding box with boxes of subcells */ - for (sub_cell_list = cell->child_cells; sub_cell_list != NULL; sub_cell_list = sub_cell_list->next) { + for (sub_cell_list = cell->child_cells; sub_cell_list != NULL; + sub_cell_list = sub_cell_list->next) { sub_cell = (struct gds_cell_instance *)sub_cell_list->data; bounding_box_prepare_empty(&temp_box); /* Recursion Woohoo!! This dies if your GDS is faulty and contains a reference loop */ calculate_cell_bounding_box(&temp_box, sub_cell->cell_ref); /* Apply transformations */ - bounding_box_apply_transform(ABS(sub_cell->magnification), sub_cell->angle, sub_cell->flipped, &temp_box); + bounding_box_apply_transform(ABS(sub_cell->magnification), sub_cell->angle, + sub_cell->flipped, &temp_box); /* Move bounding box to origin */ temp_box.vectors.lower_left.x += sub_cell->origin.x; diff --git a/widgets/conv-settings-dialog.c b/widgets/conv-settings-dialog.c index 7460dfb..d4444d7 100644 --- a/widgets/conv-settings-dialog.c +++ b/widgets/conv-settings-dialog.c @@ -18,7 +18,7 @@ */ /** - * @file conv-settings-dilaog.c + * @file conv-settings-dialog.c * @brief Implementation of the setting dialog * @author Mario Hüttel */ @@ -127,24 +127,73 @@ static gboolean shape_drawer_drawing_callback(GtkWidget *widget, cairo_t *cr, gp return FALSE; } +static double convert_number_to_engineering(double input, const char **out_prefix) +{ + const char *selected_prefix = NULL; + double return_val = 0.0; + int idx; + const static char * prefixes[] = {"y", "z", "a", "f", "p", "n", "u", "m", "c", "d", /* < 1 */ + "", /* 1 */ + "h", "k", "M", "G", "T", "P", "E", "Z", "Y"}; /* > 1 */ + const static double scale[] = {1E-24, 1E-21, 1E-18, 1E-15, 1E-12, 1E-9, 1E-6, 1E-3, 1E-2, 1E-1, + 1, + 1E2, 1E3, 1E6, 1E9, 1E12, 1E15, 1E18, 1E21, 1E24}; + const int prefix_count = (int)(sizeof(prefixes)/sizeof(char *)); + + for (idx = 1; idx < prefix_count; idx++) { + if (input < scale[idx]) { + /* This prefix is bigger than the number. Take the previous one */ + selected_prefix = prefixes[idx-1]; + return_val = input / scale[idx-1]; + break; + } + } + + /* Check if prefix was set by loop. Else take the largest in the list */ + if (selected_prefix == NULL) { + selected_prefix = prefixes[prefix_count-1]; + return_val = input / scale[prefix_count-1]; + } + + if (out_prefix) + *out_prefix = selected_prefix; + + return return_val; +} + static void renderer_settings_dialog_update_labels(RendererSettingsDialog *self) { char default_buff[100]; double scale; + double width_meters; + double height_meters; + double width_engineering; + const char *width_prefix; + double height_engineering; + const char *height_prefix; if (!self) return; - snprintf(default_buff, sizeof(default_buff), "Width: %E", self->cell_width * self->unit_in_meters); + width_meters = (double)self->cell_width * self->unit_in_meters; + height_meters = (double)self->cell_height * self->unit_in_meters; + + width_engineering = convert_number_to_engineering(width_meters, &width_prefix); + height_engineering = convert_number_to_engineering(height_meters, &height_prefix); + + snprintf(default_buff, sizeof(default_buff), "Width: %.3lf %sm", width_engineering, width_prefix); gtk_label_set_text(self->x_label, default_buff); - snprintf(default_buff, sizeof(default_buff), "Height: %E", self->cell_height * self->unit_in_meters); + snprintf(default_buff, sizeof(default_buff), "Height: %.3lf %sm", height_engineering, height_prefix); gtk_label_set_text(self->y_label, default_buff); scale = gtk_range_get_value(GTK_RANGE(self->scale)); - snprintf(default_buff, sizeof(default_buff), "Output Width: %u px", (unsigned int)((double)self->cell_width / scale)); + /* Set the pixel sizes */ + snprintf(default_buff, sizeof(default_buff), "Output Width: %u px", + (unsigned int)((double)self->cell_width / scale)); gtk_label_set_text(self->x_output_label, default_buff); - snprintf(default_buff, sizeof(default_buff), "Output Height: %u px", (unsigned int)((double)self->cell_height / scale)); + snprintf(default_buff, sizeof(default_buff), "Output Height: %u px", + (unsigned int)((double)self->cell_height / scale)); gtk_label_set_text(self->y_output_label, default_buff); } @@ -265,7 +314,6 @@ void renderer_settings_dialog_set_cell_width(RendererSettingsDialog *dialog, uns if (width == 0) width = 1; - dialog->cell_width = width; renderer_settings_dialog_update_labels(dialog); }