10 Commits

5 changed files with 259 additions and 55 deletions

View File

@@ -21,7 +21,7 @@ pkgver () {
build () {
cd "$srcdir/$pkgname-git"
cmake .
cmake -DCMAKE_BUILD_TYPE=Release .
make
}

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.8.15
# Doxyfile 1.8.16
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -197,6 +197,16 @@ SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@@ -329,7 +339,7 @@ MARKDOWN_SUPPORT = YES
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 0.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
@@ -465,6 +475,12 @@ EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
@@ -543,7 +559,7 @@ INTERNAL_DOCS = NO
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
# (including Cygwin) ands Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = NO
@@ -1402,7 +1418,7 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1410,7 +1426,7 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1419,7 +1435,7 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1427,7 +1443,7 @@ QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1435,7 +1451,7 @@ QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
@@ -1714,10 +1730,11 @@ LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX.
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: \makeindex.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = \makeindex
@@ -1854,7 +1871,7 @@ LATEX_BIB_STYLE = plain
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
LATEX_TIMESTAMP = YES
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
@@ -2209,12 +2226,6 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
@@ -2228,15 +2239,6 @@ PERL_PATH = /usr/bin/perl
CLASS_DIAGRAMS = NO
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see:
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.

View File

@@ -37,31 +37,52 @@ Development is done with the following library versions:
Go to the build directory you want to compile in. This may be the gds-render project root.
Execute
@code
cmake <Path to gds-render root>
cmake -DCMAKE_BUILD_TYPE=Release <Path to gds-render root>
@endcode
Cmake will check the dependencies. Once cmake has finished. Type
for a build in release configuartion. Use `-DCMAKE_BUILD_TYPE=Debug` for debugging. Cmake will check the dependencies.
Once cmake has finished, type
@code
make
@endcode
in order to build the program and
to build the program and
@code
make documentation
@endcode
to build the doxygen documentation.
to build the doxygen documentation.
@subsection arch-makepkg Archlinux Package
The subfolder 'AUR' contains a PKGBUILD file to build an Archlinux/Pacman package.
@subsection Compiler Warnings
@subsection comp-warnings Compiler Warnings
The compiler will throw the following warnings. Compiled with GCC 8.2.1.
| Warning | Assessment |
| ------- | ---------- |
| warning: calculate_path_miter_points defined but not used [-Wunused-function] | Ignore. Function will be used in later versions. |
@subsection windows-compilation Compilation for Windows
@warning Windows is not a target system for this application, considering that this program converts GDS files which are most likely generated under a Linux system. The tips shown in this section are a guidance for anyone trying to build this application for Windows.
@warning Note that the Windows compatibility may decrease in future releases and a simple compilation like with this version might not be possible anymore.
The current release of 'gds-render' does not compile under a windows system, due to incompatibilities in the external library renderer.
It is possible to comment out the code that causes the incompatibility. The external renderer will not be usable after this.
Steps:
- Go to file external-renderer.c
- Remove `#include` <dlfcn.h>
- comment out all code in #external_renderer_render_cell
The program should now compile.
@note In versions currently in development, the cairo renderer will be unusable under Windows due to Issue `#16`. So you might want to stick with this version.
*/

View File

@@ -79,7 +79,26 @@ enum gds_record {
BOX = 0x2D00,
LAYER = 0x0D02,
WIDTH = 0x0F03,
PATHTYPE = 0x2102
PATHTYPE = 0x2102,
COLROW = 0x1302,
AREF = 0x0B00
};
/**
* @brief Struct representing an array instantiation.
*
* This struct is defined locally because it is not exposed to the outside of the
* parser. Array references are internally converted to a bunch of standard @ref gds_cell_instance elements.
*/
struct gds_cell_array_instance {
char ref_name[CELL_NAME_MAX]; /**< @brief Name of referenced cell */
struct gds_cell *cell_ref; /**< @brief Referenced gds_cell structure */
struct gds_point control_points[3]; /**< @brief The three control points */
int flipped; /**< @brief Mirror each instance on x-axis before rotation */
double angle; /**< @brief Angle of rotation for each instance (counter clockwise) in degrees */
double magnification; /**< @brief Magnification of each instance */
int columns; /**< @brief Column count */
int rows; /**< @brief Row count */
};
/**
@@ -112,6 +131,36 @@ static int name_cell_ref(struct gds_cell_instance *cell_inst,
return 0;
}
/**
* @brief Name cell reference
* @param cell_inst Cell reference
* @param bytes Length of name
* @param data Name
* @return 0 if successful
*/
static int name_array_cell_ref(struct gds_cell_array_instance *cell_inst,
unsigned int bytes, char *data)
{
int len;
if (cell_inst == NULL) {
GDS_ERROR("Naming array cell ref with no opened cell ref");
return -1;
}
data[bytes] = 0; // Append '0'
len = (int)strlen(data);
if (len > CELL_NAME_MAX-1) {
GDS_ERROR("Cell name '%s' too long: %d\n", data, len);
return -1;
}
/* else: */
strcpy(cell_inst->ref_name, data);
GDS_INF("\tCell referenced: %s\n", cell_inst->ref_name);
return 0;
}
/**
* @brief Convert GDS 8-byte real to double
* @param data 8 Byte GDS real
@@ -133,7 +182,7 @@ static double gds_convert_double(const char *data)
if (data[i] != 0)
break;
if (i == 7) {
/* 7 bytes all 0 */
/* All 8 bytes are 0 */
return 0.0;
}
}
@@ -327,9 +376,9 @@ static GList *append_cell_ref(GList *curr_list, struct gds_cell_instance **insta
if (inst) {
inst->cell_ref = NULL;
inst->ref_name[0] = 0;
inst->magnification = 1;
inst->magnification = 1.0;
inst->flipped = 0;
inst->angle = 0;
inst->angle = 0.0;
} else
return NULL;
@@ -509,6 +558,62 @@ static void gds_parse_date(const char *buffer, int length, struct gds_time_field
}
}
/**
* @brief Convert AREF to a bunch of SREFs and append them to \p container_cell
*
* This function converts a single array reference (\p aref) to gds_cell_array_instance::rows * gds_cell_array_instance::columns
* single references (SREFs). See @ref gds_cell_instance.
*
* Both gds_cell_array_instance::rows and gds_cell_array_instance::columns must be larger than zero.
*
* @param[in] aref Array reference to parse
* @param[in] container_cell cell to add the converted single references to.
*/
static void convert_aref_to_sref(struct gds_cell_array_instance *aref, struct gds_cell *container_cell)
{
struct gds_point origin;
struct gds_point row_shift_vector;
struct gds_point col_shift_vector;
struct gds_cell_instance *sref_inst = NULL;
int col;
int row;
if (!aref || !container_cell)
return;
if (aref->columns == 0 || aref->rows == 0) {
GDS_ERROR("Conversion of array instance aborted. No rows / columns.");
return;
}
origin.x = aref->control_points[0].x;
origin.y = aref->control_points[0].y;
row_shift_vector.x = (aref->control_points[2].x - origin.x) / aref->rows;
row_shift_vector.y = (aref->control_points[2].y - origin.y) / aref->rows;
col_shift_vector.x = (aref->control_points[1].x - origin.x) / aref->columns;
col_shift_vector.y = (aref->control_points[1].y - origin.y) / aref->columns;
/* Iterate over columns and rows */
for (col = 0; col < aref->columns; col++) {
for (row = 0; row < aref->rows; row++) {
/* Create new instance for this row/column and aconfigure data */
container_cell->child_cells = append_cell_ref(container_cell->child_cells, &sref_inst);
if (!sref_inst) {
GDS_ERROR("Appending cell ref failed!");
continue;
}
sref_inst->angle = aref->angle;
sref_inst->magnification = aref->magnification;
sref_inst->flipped = aref->flipped;
strncpy(sref_inst->ref_name, aref->ref_name, CELL_NAME_MAX);
sref_inst->origin.x = origin.x + row_shift_vector.x * row + col_shift_vector.x * col;
sref_inst->origin.y = origin.y + row_shift_vector.y * row + col_shift_vector.y * col;
}
}
GDS_INF("Converted AREF to SREFs\n");
}
int parse_gds_from_file(const char *filename, GList **library_list)
{
char *workbuff;
@@ -522,6 +627,8 @@ int parse_gds_from_file(const char *filename, GList **library_list)
struct gds_cell *current_cell = NULL;
struct gds_graphics *current_graphics = NULL;
struct gds_cell_instance *current_s_reference = NULL;
struct gds_cell_array_instance *current_a_reference = NULL;
struct gds_cell_array_instance temp_a_reference;
int x, y;
////////////
GList *lib_list;
@@ -583,6 +690,7 @@ int parse_gds_from_file(const char *filename, GList **library_list)
}
rec_type = gds_convert_unsigend_int16(workbuff);
/* if begin: Allocate structures */
switch (rec_type) {
case BGNLIB:
@@ -626,7 +734,7 @@ int parse_gds_from_file(const char *filename, GList **library_list)
current_cell->parent_library = current_lib;
GDS_INF("Entering Cell\n");
GDS_INF("Entering cell\n");
break;
case ENDSTR:
if (current_cell == NULL) {
@@ -693,7 +801,6 @@ int parse_gds_from_file(const char *filename, GList **library_list)
break;
case ENDEL:
if (current_graphics != NULL) {
GDS_INF("\tLeaving %s\n", (current_graphics->gfx_type == GRAPHIC_POLYGON ? "boundary" : "path"));
current_graphics = NULL;
}
@@ -701,6 +808,12 @@ int parse_gds_from_file(const char *filename, GList **library_list)
GDS_INF("\tLeaving Reference\n");
current_s_reference = NULL;
}
if (current_a_reference != NULL) {
GDS_INF("\tLeaving Array Reference\n");
convert_aref_to_sref(current_a_reference, current_cell);
current_a_reference = NULL;
}
break;
case XY:
if (current_graphics) {
@@ -709,24 +822,51 @@ int parse_gds_from_file(const char *filename, GList **library_list)
if (rec_data_length != 8) {
GDS_WARN("Instance has weird coordinates. Rendered output might be screwed!");
}
} else if (current_a_reference) {
if (rec_data_length != (3*(4+4)))
GDS_WARN("Array instance has weird coordinates. Rendered output might be screwed!");
}
break;
case AREF:
if (current_cell == NULL) {
GDS_ERROR("Cell array reference outside of cell");
run = -3;
break;
}
break;
if (current_a_reference != NULL) {
GDS_ERROR("Recursive cell array reference");
run = -3;
break;
}
GDS_INF("Entering Array Reference\n");
/* Array references are coverted after fully declared. Therefore,
* only a static buffer is needed
*/
current_a_reference = &temp_a_reference;
current_a_reference->ref_name[0] = '\0';
current_a_reference->angle = 0.0;
current_a_reference->magnification = 1.0;
current_a_reference->flipped = 0;
current_a_reference->rows = 0;
current_a_reference->columns = 0;
break;
case COLROW:
case MAG:
break;
case ANGLE:
break;
case STRANS:
break;
case WIDTH:
break;
case PATHTYPE:
break;
case UNITS:
case LIBNAME:
case SNAME:
case LAYER:
case STRNAME:
break;
default:
//GDS_WARN("Record: %04x, len: %u", (unsigned int)rec_type, (unsigned int)rec_data_length);
GDS_INF("Unhandled Record: %04x, len: %u\n", (unsigned int)rec_type, (unsigned int)rec_data_length);
break;
} /* switch(rec_type) */
@@ -744,7 +884,7 @@ int parse_gds_from_file(const char *filename, GList **library_list)
}
switch (rec_type) {
case AREF:
case HEADER:
case ENDLIB:
case ENDSTR:
@@ -756,6 +896,20 @@ int parse_gds_from_file(const char *filename, GList **library_list)
case INVALID:
break;
case COLROW:
if (!current_a_reference) {
GDS_ERROR("COLROW record defined outside of array instance");
break;
}
if (rec_data_length != 4 || read != 4) {
GDS_ERROR("COLUMN/ROW count record contains too few data. Won't set column and row counts (%d, %d)",
rec_data_length, read);
break;
}
current_a_reference->columns = (int)gds_convert_signed_int16(&workbuff[0]);
current_a_reference->rows = (int)gds_convert_signed_int16(&workbuff[2]);
GDS_INF("\tRows: %d\n\tColumns: %d\n", current_a_reference->rows, current_a_reference->columns);
break;
case UNITS:
if (!current_lib) {
GDS_WARN("Units defined outside of library!\n");
@@ -799,19 +953,31 @@ int parse_gds_from_file(const char *filename, GList **library_list)
GDS_INF("\t\tSet coordinate: %d/%d\n", x, y);
}
} else if (current_a_reference) {
for (i = 0; i < 3; i++) {
x = gds_convert_signed_int(&workbuff[i*8]);
y = gds_convert_signed_int(&workbuff[i*8+4]);
current_a_reference->control_points[i].x = x;
current_a_reference->control_points[i].y = y;
GDS_INF("\tSet control point %d: %d/%d\n", i, x, y);
}
}
break;
case STRANS:
if (!current_s_reference) {
if (current_s_reference) {
current_s_reference->flipped = ((workbuff[0] & 0x80) ? 1 : 0);
} else if (current_a_reference) {
current_a_reference->flipped = ((workbuff[0] & 0x80) ? 1 : 0);
} else {
GDS_ERROR("Transformation defined outside of instance");
break;
}
current_s_reference->flipped = ((workbuff[0] & 0x80) ? 1 : 0);
break;
case SNAME:
if (current_s_reference) {
name_cell_ref(current_s_reference, (unsigned int)read, workbuff);
} else if (current_a_reference) {
name_array_cell_ref(current_a_reference, (unsigned int)read, workbuff);
} else {
GDS_ERROR("reference name set outside of cell reference.\n");
}
@@ -846,12 +1012,16 @@ int parse_gds_from_file(const char *filename, GList **library_list)
current_s_reference->magnification = gds_convert_double(workbuff);
GDS_INF("\t\tMagnification defined: %lf\n", current_s_reference->magnification);
}
if (current_a_reference != NULL) {
current_a_reference->magnification = gds_convert_double(workbuff);
GDS_INF("\t\tMagnification defined: %lf\n", current_a_reference->magnification);
}
break;
case ANGLE:
if (rec_data_length != 8) {
GDS_WARN("Angle is not an 8 byte real. Results may be wrong");
}
if (current_graphics != NULL && current_s_reference != NULL) {
if (current_graphics != NULL && current_s_reference != NULL && current_a_reference != NULL) {
GDS_ERROR("Open Graphics and Cell Reference\n\tMissing ENDEL?");
run = -6;
break;
@@ -860,6 +1030,10 @@ int parse_gds_from_file(const char *filename, GList **library_list)
current_s_reference->angle = gds_convert_double(workbuff);
GDS_INF("\t\tAngle defined: %lf\n", current_s_reference->angle);
}
if (current_a_reference != NULL) {
current_a_reference->angle = gds_convert_double(workbuff);
GDS_INF("\t\tAngle defined: %lf\n", current_a_reference->angle);
}
break;
case PATHTYPE:
if (current_graphics == NULL) {

View File

@@ -176,6 +176,17 @@ int gds_tree_check_reference_loops(struct gds_library *lib)
/* iterate through references and check if loop exists */
res = gds_tree_check_iterate_ref_and_check(cell_to_check, &visited_cells);
if (visited_cells) {
/* If cell contains no loop, print error when list not empty.
* In case of a loop, it is completely normal that the list is not empty,
* due to the instant return from gds_tree_check_iterate_ref_and_check()
*/
if (res == 0)
fprintf(stderr, "Visited cell list should be empty. This is a bug. Please report this.\n");
g_list_free(visited_cells);
visited_cells = NULL;
}
if (res < 0) {
/* Error */
return res;
@@ -190,10 +201,6 @@ int gds_tree_check_reference_loops(struct gds_library *lib)
}
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;
}