2018-05-10 01:43:14 +02:00
|
|
|
/*
|
2018-05-15 22:54:10 +02:00
|
|
|
* GDSII-Converter
|
2018-05-10 01:43:14 +02:00
|
|
|
* Copyright (C) 2018 Mario Hüttel <mario.huettel@gmx.net>
|
|
|
|
*
|
|
|
|
* This file is part of GDSII-Converter.
|
|
|
|
*
|
|
|
|
* GDSII-Converter is free software: you can redistribute it and/or modify
|
2018-05-16 16:29:34 +02:00
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
2018-05-10 01:43:14 +02:00
|
|
|
*
|
2018-05-15 22:54:10 +02:00
|
|
|
* GDSII-Converter is distributed in the hope that it will be useful,
|
2018-05-10 01:43:14 +02:00
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-07-24 17:45:16 +02:00
|
|
|
/**
|
|
|
|
* @file latex-output.c
|
|
|
|
* @brief LaTeX output renderer
|
|
|
|
* @author Mario Hüttel <mario.huettel@gmx.net>
|
|
|
|
*/
|
|
|
|
|
2018-05-22 16:17:14 +02:00
|
|
|
#include "latex-output.h"
|
2018-06-02 02:54:38 +02:00
|
|
|
#include <math.h>
|
2018-05-07 13:27:50 +02:00
|
|
|
|
2018-07-24 17:45:16 +02:00
|
|
|
/**
|
|
|
|
* @addtogroup LaTeX-Renderer
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @brief Writes a GString \p buffer to the fixed file tex_file */
|
2018-05-22 18:45:23 +02:00
|
|
|
#define WRITEOUT_BUFFER(buff) fwrite((buff)->str, sizeof(char), (buff)->len, tex_file)
|
|
|
|
|
2018-07-24 17:45:16 +02:00
|
|
|
/**
|
|
|
|
* @brief Write the layer declarration to TeX file
|
|
|
|
*
|
|
|
|
* This writes the declaration of the layers and the mapping in which order
|
|
|
|
* the layers shall be rendered by TikZ. Layers are written in the order they are
|
|
|
|
* positioned inside the \p layer_infos list.
|
|
|
|
*
|
|
|
|
* @param tex_file TeX-File to write to
|
|
|
|
* @param layer_infos List containing layer_info structs.
|
|
|
|
* @param buffer
|
|
|
|
* @note The field layer_info::stacked_position is ignored. Stack depends on list order.
|
|
|
|
*/
|
2018-05-22 18:45:23 +02:00
|
|
|
static void write_layer_definitions(FILE *tex_file, GList *layer_infos, GString *buffer)
|
|
|
|
{
|
|
|
|
GList *list;
|
|
|
|
struct layer_info *lifo;
|
|
|
|
char *end_str;
|
|
|
|
|
|
|
|
for (list = layer_infos; list != NULL; list = list->next) {
|
|
|
|
lifo = (struct layer_info *)list->data;
|
2018-05-23 13:17:10 +02:00
|
|
|
g_string_printf(buffer, "\\pgfdeclarelayer{l%d}\n\\definecolor{c%d}{rgb}{%lf,%lf,%lf}\n",
|
|
|
|
lifo->layer, lifo->layer,
|
|
|
|
lifo->color.red, lifo->color.green, lifo->color.blue);
|
2018-05-22 18:45:23 +02:00
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_string_printf(buffer, "\\pgfsetlayers{");
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
|
|
|
|
for (list = layer_infos; list != NULL; list = list->next) {
|
|
|
|
lifo = (struct layer_info *)list->data;
|
|
|
|
|
|
|
|
if (list->next == NULL)
|
|
|
|
end_str = ",main}";
|
|
|
|
else
|
|
|
|
end_str = ",";
|
|
|
|
g_string_printf(buffer, "l%d%s", lifo->layer, end_str);
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
}
|
|
|
|
fwrite("\n", sizeof(char), 1, tex_file);
|
|
|
|
}
|
|
|
|
|
2018-05-23 13:17:10 +02:00
|
|
|
/**
|
2018-07-24 17:45:16 +02:00
|
|
|
* @brief Write layer Envirmonment
|
|
|
|
*
|
|
|
|
* If the requested layer shall be rendered, this code writes the necessary code
|
|
|
|
* to open the layer. It also returns the color the layer shall be rendered in.
|
|
|
|
*
|
|
|
|
* The followingenvironments are generated:
|
|
|
|
*
|
|
|
|
* @code{.tex}
|
|
|
|
* \begin{pgfonlayer}{<layer>}
|
|
|
|
* % If pdf layers shall be used also this is enabled:
|
|
|
|
* \begin{scope}[ocg={ref=<layer>, status=visible,name={<Layer Name>}}]
|
|
|
|
* @endcode
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* If the layer shall not be rendered, FALSE is returned and the color is not filled in and
|
|
|
|
* the cod eis not written to the file.
|
|
|
|
*
|
|
|
|
* @param tex_file TeX file to write to
|
|
|
|
* @param color Return of the layer's color
|
|
|
|
* @param layer Requested layer number
|
|
|
|
* @param linfo Layer information list containing layer_info structs
|
|
|
|
* @param buffer Some working buffer
|
|
|
|
* @return TRUE, if the layer shall be rendered.
|
|
|
|
* @note The opened environments have to be closed afterwards
|
2018-05-23 13:17:10 +02:00
|
|
|
*/
|
|
|
|
static gboolean write_layer_env(FILE *tex_file, GdkRGBA *color, int layer, GList *linfo, GString *buffer)
|
2018-05-22 18:45:23 +02:00
|
|
|
{
|
2018-05-23 13:17:10 +02:00
|
|
|
GList *temp;
|
|
|
|
struct layer_info *inf;
|
|
|
|
|
|
|
|
for (temp = linfo; temp != NULL; temp = temp->next) {
|
|
|
|
inf = (struct layer_info *)temp->data;
|
|
|
|
if (inf->layer == layer) {
|
|
|
|
color->alpha = inf->color.alpha;
|
|
|
|
color->red = inf->color.red;
|
|
|
|
color->green = inf->color.green;
|
|
|
|
color->blue = inf->color.blue;
|
2018-06-01 21:47:54 +02:00
|
|
|
g_string_printf(buffer, "\\begin{pgfonlayer}{l%d}\n\\ifcreatepdflayers\n\\begin{scope}[ocg={ref=%d, status=visible,name={%s}}]\n\\fi]\n",
|
2018-05-31 00:04:14 +02:00
|
|
|
layer, layer, inf->name);
|
2018-05-23 13:17:10 +02:00
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
2018-05-22 18:45:23 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 17:45:16 +02:00
|
|
|
/**
|
|
|
|
* @brief Writes a graphics object to the specified tex_file
|
|
|
|
*
|
|
|
|
* This function opens the layer, writes a graphics object and closes the layer
|
|
|
|
*
|
|
|
|
* @param tex_file File to write to
|
|
|
|
* @param graphics Object to render
|
|
|
|
* @param linfo Layer information
|
|
|
|
* @param buffer Working buffer
|
|
|
|
* @param scale Scale abject down by this value
|
|
|
|
*/
|
2018-07-19 17:54:05 +02:00
|
|
|
static void generate_graphics(FILE *tex_file, GList *graphics, GList *linfo, GString *buffer, double scale)
|
2018-05-22 18:45:23 +02:00
|
|
|
{
|
2018-05-23 13:17:10 +02:00
|
|
|
GList *temp;
|
|
|
|
GList *temp_vertex;
|
|
|
|
struct gds_graphics *gfx;
|
|
|
|
struct gds_point *pt;
|
|
|
|
GdkRGBA color;
|
2018-06-02 02:54:38 +02:00
|
|
|
static const char *line_caps[] = {"butt", "round", "rect"};
|
2018-05-23 13:17:10 +02:00
|
|
|
|
|
|
|
for (temp = graphics; temp != NULL; temp = temp->next) {
|
|
|
|
gfx = (struct gds_graphics *)temp->data;
|
|
|
|
if (write_layer_env(tex_file, &color, (int)gfx->layer, linfo, buffer) == TRUE) {
|
|
|
|
|
|
|
|
/* Layer is defined => create graphics */
|
2018-07-17 00:02:30 +02:00
|
|
|
if (gfx->gfx_type == GRAPHIC_POLYGON || gfx->gfx_type == GRAPHIC_BOX ) {
|
2018-05-23 13:17:10 +02:00
|
|
|
g_string_printf(buffer, "\\draw[line width=0.00001 pt, draw={c%d}, fill={c%d}, fill opacity={%lf}] ",
|
|
|
|
gfx->layer, gfx->layer, color.alpha);
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
/* Append vertices */
|
|
|
|
for (temp_vertex = gfx->vertices; temp_vertex != NULL; temp_vertex = temp_vertex->next) {
|
|
|
|
pt = (struct gds_point *)temp_vertex->data;
|
2018-07-19 17:54:05 +02:00
|
|
|
g_string_printf(buffer, "(%lf pt, %lf pt) -- ", ((double)pt->x)/scale, ((double)pt->y)/scale);
|
2018-05-23 13:17:10 +02:00
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
}
|
|
|
|
g_string_printf(buffer, "cycle;\n");
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
2018-07-17 00:02:30 +02:00
|
|
|
} else if (gfx->gfx_type == GRAPHIC_PATH) {
|
2018-06-02 02:54:38 +02:00
|
|
|
|
|
|
|
if (g_list_length(gfx->vertices) < 2) {
|
|
|
|
printf("Cannot write path with less than 2 points\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gfx->path_render_type < 0 || gfx->path_render_type > 2) {
|
|
|
|
printf("Path type unrecognized. Setting to 'flushed'\n");
|
2018-07-20 22:36:54 +02:00
|
|
|
gfx->path_render_type = PATH_FLUSH;
|
2018-06-02 02:54:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_string_printf(buffer, "\\draw[line width=%lf pt, draw={c%d}, opacity={%lf}, cap=%s] ",
|
2018-07-19 17:54:05 +02:00
|
|
|
gfx->width_absolute/scale, gfx->layer, color.alpha,
|
2018-06-02 02:54:38 +02:00
|
|
|
line_caps[gfx->path_render_type]);
|
2018-06-01 21:47:54 +02:00
|
|
|
WRITEOUT_BUFFER(buffer);
|
2018-06-02 02:54:38 +02:00
|
|
|
|
2018-06-01 21:47:54 +02:00
|
|
|
/* Append vertices */
|
|
|
|
for (temp_vertex = gfx->vertices; temp_vertex != NULL; temp_vertex = temp_vertex->next) {
|
|
|
|
pt = (struct gds_point *)temp_vertex->data;
|
|
|
|
g_string_printf(buffer, "(%lf pt, %lf pt)%s",
|
2018-07-19 17:54:05 +02:00
|
|
|
((double)pt->x)/scale,
|
|
|
|
((double)pt->y)/scale,
|
2018-06-01 21:47:54 +02:00
|
|
|
(temp_vertex->next ? " -- " : ""));
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
}
|
|
|
|
g_string_printf(buffer, ";\n");
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
2018-05-23 13:17:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-01 21:47:54 +02:00
|
|
|
g_string_printf(buffer, "\\ifcreatepdflayers\n\\end{scope}\n\\fi\n\\end{pgfonlayer}\n");
|
2018-05-23 13:17:10 +02:00
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* For graphics */
|
|
|
|
}
|
|
|
|
|
2018-07-24 17:45:16 +02:00
|
|
|
/**
|
|
|
|
* @brief Render cell to file
|
|
|
|
* @param cell Cell to render
|
|
|
|
* @param layer_infos Layer information
|
|
|
|
* @param tex_file File to write to
|
|
|
|
* @param buffer Working buffer
|
|
|
|
* @param scale Scale output down by this value
|
|
|
|
*/
|
2018-07-19 17:54:05 +02:00
|
|
|
static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, GString *buffer, double scale)
|
2018-05-23 13:17:10 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
GList *list_child;
|
|
|
|
struct gds_cell_instance *inst;
|
|
|
|
|
|
|
|
/* Draw polygons of current cell */
|
2018-07-19 17:54:05 +02:00
|
|
|
generate_graphics(tex_file, cell->graphic_objs, layer_infos, buffer, scale);
|
2018-05-23 13:17:10 +02:00
|
|
|
|
|
|
|
/* Draw polygons of childs */
|
|
|
|
for (list_child = cell->child_cells; list_child != NULL; list_child = list_child->next) {
|
|
|
|
inst = (struct gds_cell_instance *)list_child->data;
|
2018-07-04 09:49:04 +02:00
|
|
|
|
|
|
|
/* Abort if cell has no reference */
|
|
|
|
if (!inst->cell_ref)
|
|
|
|
continue;
|
|
|
|
|
2018-05-23 13:17:10 +02:00
|
|
|
/* generate translation scope */
|
2018-06-12 18:33:13 +02:00
|
|
|
g_string_printf(buffer, "\\begin{scope}[shift={(%lf pt,%lf pt)}]\n",
|
2018-07-19 17:54:05 +02:00
|
|
|
((double)inst->origin.x)/scale,((double)inst->origin.y)/scale);
|
2018-05-23 13:17:10 +02:00
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
|
2018-06-12 18:33:13 +02:00
|
|
|
g_string_printf(buffer, "\\begin{scope}[rotate=%lf]\n", inst->angle);
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
|
2018-07-04 21:46:39 +02:00
|
|
|
g_string_printf(buffer, "\\begin{scope}[yscale=%lf, xscale=%lf]\n", (inst->flipped ? -1*inst->magnification : inst->magnification),
|
2018-07-17 00:02:30 +02:00
|
|
|
inst->magnification);
|
2018-05-23 13:17:10 +02:00
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
|
2018-07-19 17:54:05 +02:00
|
|
|
render_cell(inst->cell_ref, layer_infos, tex_file, buffer, scale);
|
2018-05-23 13:17:10 +02:00
|
|
|
|
|
|
|
g_string_printf(buffer, "\\end{scope}\n");
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
|
|
|
|
|
|
|
g_string_printf(buffer, "\\end{scope}\n");
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
2018-06-12 18:33:13 +02:00
|
|
|
|
|
|
|
g_string_printf(buffer, "\\end{scope}\n");
|
|
|
|
WRITEOUT_BUFFER(buffer);
|
2018-05-23 13:17:10 +02:00
|
|
|
}
|
|
|
|
|
2018-05-22 18:45:23 +02:00
|
|
|
}
|
|
|
|
|
2018-07-19 22:09:58 +02:00
|
|
|
void latex_render_cell_to_code(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, double scale,
|
|
|
|
gboolean create_pdf_layers, gboolean standalone_document)
|
2018-05-22 16:17:14 +02:00
|
|
|
{
|
2018-05-22 18:45:23 +02:00
|
|
|
GString *working_line;
|
|
|
|
|
2018-05-23 13:17:10 +02:00
|
|
|
|
2018-05-22 18:45:23 +02:00
|
|
|
if (!tex_file || !layer_infos || !cell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* 10 kB Line working buffer should be enough */
|
|
|
|
working_line = g_string_new_len(NULL, LATEX_LINE_BUFFER_KB*1024);
|
|
|
|
|
2018-05-31 02:16:37 +02:00
|
|
|
/* standalone foo */
|
2018-07-19 22:09:58 +02:00
|
|
|
g_string_printf(working_line, "\\newif\\iftestmode\n\\testmode%s\n",
|
|
|
|
(standalone_document ? "true" : "false"));
|
2018-05-31 02:16:37 +02:00
|
|
|
WRITEOUT_BUFFER(working_line);
|
2018-07-19 22:09:58 +02:00
|
|
|
g_string_printf(working_line, "\\newif\\ifcreatepdflayers\n\\createpdflayers%s\n",
|
|
|
|
(create_pdf_layers ? "true" : "false"));
|
2018-06-01 21:47:54 +02:00
|
|
|
WRITEOUT_BUFFER(working_line);
|
2018-05-31 02:16:37 +02:00
|
|
|
g_string_printf(working_line, "\\iftestmode\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
|
|
|
g_string_printf(working_line, "\\documentclass[tikz]{standalone}\n\\usepackage{xcolor}\n\\usetikzlibrary{ocgx}\n\\begin{document}\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
|
|
|
g_string_printf(working_line, "\\fi\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
|
|
|
|
2018-05-22 18:45:23 +02:00
|
|
|
/* Write layer definitions */
|
|
|
|
write_layer_definitions(tex_file, layer_infos, working_line);
|
|
|
|
|
|
|
|
/* Open tikz Pictute */
|
|
|
|
g_string_printf(working_line, "\\begin{tikzpicture}\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
|
|
|
|
|
|
|
/* Generate graphics output */
|
2018-07-19 17:54:05 +02:00
|
|
|
render_cell(cell, layer_infos, tex_file, working_line, scale);
|
2018-05-22 18:45:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
g_string_printf(working_line, "\\end{tikzpicture}\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
2018-05-07 13:27:50 +02:00
|
|
|
|
2018-05-31 02:16:37 +02:00
|
|
|
g_string_printf(working_line, "\\iftestmode\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
|
|
|
g_string_printf(working_line, "\\end{document}\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
|
|
|
g_string_printf(working_line, "\\fi\n");
|
|
|
|
WRITEOUT_BUFFER(working_line);
|
|
|
|
|
2018-05-22 18:45:23 +02:00
|
|
|
fflush(tex_file);
|
|
|
|
g_string_free(working_line, TRUE);
|
2018-05-22 16:17:14 +02:00
|
|
|
}
|
2018-07-24 17:45:16 +02:00
|
|
|
|
|
|
|
/** @} */
|