pcb 4.1.1
An interactive printed circuit board layout editor.

nelma.c

Go to the documentation of this file.
00001 /*
00002  *                            COPYRIGHT
00003  *
00004  *  PCB, interactive printed circuit board design
00005  *
00006  *  NELMA (Numerical capacitance calculator) export HID
00007  *  Copyright (C) 2006 Tomaz Solc (tomaz.solc@tablix.org)
00008  *
00009  *  PNG export code is based on the PNG export HID
00010  *  Copyright (C) 2006 Dan McMahill
00011  *
00012  *  This program is free software; you can redistribute it and/or modify
00013  *  it under the terms of the GNU General Public License as published by
00014  *  the Free Software Foundation; either version 2 of the License, or
00015  *  (at your option) any later version.
00016  *
00017  *  This program is distributed in the hope that it will be useful,
00018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  *  GNU General Public License for more details.
00021  *
00022  *  You should have received a copy of the GNU General Public License along
00023  *  with this program; if not, write to the Free Software Foundation, Inc.,
00024  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00025  *
00026  */
00027 
00028 /*
00029  * This HID exports a PCB layout into: o One layer mask file (PNG format) per
00030  * copper layer. o Nelma configuration file that contains netlist and pin
00031  * information.
00032  */
00033 
00034 /*
00035  * FIXME:
00036  * 
00037  * If you have a section of a net that does not contain any pins then that
00038  * section will be missing from the Nelma's copper geometry.
00039  * 
00040  * For example:
00041  * 
00042  * this section will be ignored by Nelma |       |
00043  * 
00044  * ||             ||=======||            ||     component layer ||
00045  * ||       ||            || ||=============||       ||============||
00046  * solder layer
00047  * 
00048  * pin1           via      via           pin2
00049  * 
00050  * Single layer layouts are always exported correctly.
00051  * 
00052  */
00053 
00054 #ifdef HAVE_CONFIG_H
00055 #include "config.h"
00056 #endif
00057 
00058 #include <stdio.h>
00059 #include <stdarg.h>
00060 #include <stdlib.h>
00061 #include <string.h>
00062 #include <assert.h>
00063 
00064 #include <time.h>
00065 
00066 #include "global.h"
00067 #include "error.h" /* Message() */
00068 #include "data.h"
00069 #include "misc.h"
00070 #include "rats.h"
00071 
00072 #include "hid.h"
00073 #include "hid_draw.h"
00074 #include "../hidint.h"
00075 #include "hid/common/hidnogui.h"
00076 #include "hid/common/draw_helpers.h"
00077 
00078 #include <gd.h>
00079 
00080 #include "hid/common/hidinit.h"
00081 
00082 #ifdef HAVE_LIBDMALLOC
00083 #include <dmalloc.h>
00084 #endif
00085 
00086 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort()
00087 
00088 /* Needed for PNG export */
00089 
00090 struct color_struct {
00091         /* the descriptor used by the gd library */
00092         int             c;
00093 
00094         /* so I can figure out what rgb value c refers to */
00095         unsigned int    r, g, b;
00096 };
00097 
00098 struct hid_gc_struct {
00099         HID            *me_pointer;
00100         EndCapStyle     cap;
00101         Coord           width;
00102         unsigned char   r, g, b;
00103         int             erase;
00104         struct color_struct *color;
00105         gdImagePtr      brush;
00106 };
00107 
00108 static HID nelma_hid;
00109 static HID_DRAW nelma_graphics;
00110 
00111 static struct color_struct *black = NULL, *white = NULL;
00112 static Coord    linewidth = -1;
00113 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1);
00114 
00115 /* gd image and file for PNG export */
00116 static gdImagePtr nelma_im = NULL;
00117 static FILE    *nelma_f = NULL;
00118 
00119 static int      is_mask;
00120 static int      is_drill;
00121 
00122 /*
00123  * Which groups of layers to export into PNG layer masks. 1 means export, 0
00124  * means do not export.
00125  */
00126 static int      nelma_export_group[MAX_GROUP];
00127 
00128 /* Group that is currently exported. */
00129 static int      nelma_cur_group;
00130 
00131 /* Filename prefix that will be used when saving files. */
00132 static const char *nelma_basename = NULL;
00133 
00134 /* Horizontal DPI (grid points per inch) */
00135 static int      nelma_dpi = -1;
00136 
00137 /* Height of the copper layers in micrometers. */
00138 
00139 /*
00140  * The height of the copper layer is currently taken as the vertical grid
00141  * step, since this is the smallest vertical feature in the layout.
00142  */
00143 static int      nelma_copperh = -1;
00144 /* Height of the substrate layers in micrometers. */
00145 static int      nelma_substrateh = -1;
00146 /* Relative permittivity of the substrate. */
00147 static double   nelma_substratee = -1;
00148 
00149 /* Permittivity of empty space (As/Vm) */
00150 static const double nelma_air_epsilon = 8.85e-12;
00151 
00152 HID_Attribute   nelma_attribute_list[] = {
00153         /* other HIDs expect this to be first.  */
00154 
00155 /* %start-doc options "94 Nelma Options"
00156 @ftable @code
00157 @item --basename <string>
00158 File name prefix.
00159 @end ftable
00160 %end-doc
00161 */
00162         {"basename", "File name prefix",
00163         HID_String, 0, 0, {0, 0, 0}, 0, 0},
00164 #define HA_basename 0
00165 
00166 /* %start-doc options "94 Nelma Options"
00167 @ftable @code
00168 @item --dpi <num>
00169 Horizontal scale factor (grid points/inch).
00170 @end ftable
00171 %end-doc
00172 */
00173         {"dpi", "Horizontal scale factor (grid points/inch)",
00174         HID_Integer, 0, 1000, {100, 0, 0}, 0, 0},
00175 #define HA_dpi 1
00176 
00177 /* %start-doc options "94 Nelma Options"
00178 @ftable @code
00179 @item --copper-height <num>
00180 Copper layer height (um).
00181 @end ftable
00182 %end-doc
00183 */
00184         {"copper-height", "Copper layer height (um)",
00185         HID_Integer, 0, 200, {100, 0, 0}, 0, 0},
00186 #define HA_copperh 2
00187 
00188 /* %start-doc options "94 Nelma Options"
00189 @ftable @code
00190 @item --substrate-height <num>
00191 Substrate layer height (um).
00192 @end ftable
00193 %end-doc
00194 */
00195         {"substrate-height", "Substrate layer height (um)",
00196         HID_Integer, 0, 10000, {2000, 0, 0}, 0, 0},
00197 #define HA_substrateh 3
00198 
00199 /* %start-doc options "94 Nelma Options"
00200 @ftable @code
00201 @item --substrate-epsilon <num>
00202 Substrate relative epsilon.
00203 @end ftable
00204 %end-doc
00205 */
00206         {"substrate-epsilon", "Substrate relative epsilon",
00207         HID_Real, 0, 100, {0, 0, 4.0}, 0, 0},
00208 #define HA_substratee 4
00209 };
00210 
00211 #define NUM_OPTIONS (sizeof(nelma_attribute_list)/sizeof(nelma_attribute_list[0]))
00212 
00213 REGISTER_ATTRIBUTES(nelma_attribute_list)
00214         static HID_Attr_Val nelma_values[NUM_OPTIONS];
00215 
00216 /* *** Utility funcions **************************************************** */
00217 
00218 /* convert from default PCB units to nelma units */
00219 static int pcb_to_nelma (Coord pcb)
00220 {
00221   return COORD_TO_INCH(pcb) * nelma_dpi;
00222 }
00223 
00224 static char    *
00225 nelma_get_png_name(const char *basename, const char *suffix)
00226 {
00227         char           *buf;
00228         int             len;
00229 
00230         len = strlen(basename) + strlen(suffix) + 6;
00231         buf = (char *)malloc(sizeof(*buf) * len);
00232 
00233         sprintf(buf, "%s.%s.png", basename, suffix);
00234 
00235         return buf;
00236 }
00237 
00238 /* Retrieves coordinates (in default PCB units) of a pin or pad. */
00239 /* Copied from netlist.c */
00240 static int 
00241 pin_name_to_xy (LibraryEntryType * pin, Coord *x, Coord *y)
00242 {
00243         ConnectionType  conn;
00244         if (!SeekPad(pin, &conn, false))
00245                 return 1;
00246         switch (conn.type) {
00247         case PIN_TYPE:
00248                 *x = ((PinType *) (conn.ptr2))->X;
00249                 *y = ((PinType *) (conn.ptr2))->Y;
00250                 return 0;
00251         case PAD_TYPE:
00252                 *x = ((PadType *) (conn.ptr2))->Point1.X;
00253                 *y = ((PadType *) (conn.ptr2))->Point1.Y;
00254                 return 0;
00255         }
00256         return 1;
00257 }
00258 
00259 /* *** Exporting netlist data and geometry to the nelma config file ******** */
00260 
00261 static void 
00262 nelma_write_space(FILE * out)
00263 {
00264         double          xh, zh;
00265 
00266         int             z;
00267         int             i, idx;
00268         const char     *ext;
00269 
00270         xh = 2.54e-2 / ((double) nelma_dpi);
00271         zh = nelma_copperh * 1e-6;
00272 
00273         fprintf(out, "\n/* **** Space **** */\n\n");
00274 
00275         fprintf(out, "space pcb {\n");
00276         fprintf(out, "\tstep = { %e, %e, %e }\n", xh, xh, zh);
00277         fprintf(out, "\tlayers = {\n");
00278 
00279         fprintf(out, "\t\t\"air-top\",\n");
00280         fprintf(out, "\t\t\"air-bottom\"");
00281 
00282         z = 10;
00283         for (i = 0; i < MAX_GROUP; i++)
00284                 if (nelma_export_group[i]) {
00285                         idx = (i >= 0 && i < max_group) ?
00286                                 PCB->LayerGroups.Entries[i][0] : i;
00287                         ext = layer_type_to_file_name(idx, FNS_fixed);
00288 
00289                         if (z != 10) {
00290                                 fprintf(out, ",\n");
00291                                 fprintf(out, "\t\t\"substrate-%d\"", z);
00292                                 z++;
00293                         }
00294                         fprintf(out, ",\n");
00295                         fprintf(out, "\t\t\"%s\"", ext);
00296                         z++;
00297                 }
00298         fprintf(out, "\n\t}\n");
00299         fprintf(out, "}\n");
00300 }
00301 
00302 
00303 static void 
00304 nelma_write_material(FILE * out, char *name, char *type, double e)
00305 {
00306         fprintf(out, "material %s {\n", name);
00307         fprintf(out, "\ttype = \"%s\"\n", type);
00308         fprintf(out, "\tpermittivity = %e\n", e);
00309         fprintf(out, "\tconductivity = 0.0\n");
00310         fprintf(out, "\tpermeability = 0.0\n");
00311         fprintf(out, "}\n");
00312 }
00313 
00314 static void 
00315 nelma_write_materials(FILE * out)
00316 {
00317         fprintf(out, "\n/* **** Materials **** */\n\n");
00318 
00319         nelma_write_material(out, "copper", "metal", nelma_air_epsilon);
00320         nelma_write_material(out, "air", "dielectric", nelma_air_epsilon);
00321         nelma_write_material(out, "composite", "dielectric",
00322                              nelma_air_epsilon * nelma_substratee);
00323 }
00324 
00325 static void 
00326 nelma_write_nets(FILE * out)
00327 {
00328         LibraryType     netlist;
00329         LibraryMenuType *net;
00330         LibraryEntryType *pin;
00331 
00332         int             n, m, i, idx;
00333 
00334         const char     *ext;
00335 
00336         netlist = PCB->NetlistLib;
00337 
00338         fprintf(out, "\n/* **** Nets **** */\n\n");
00339 
00340         for (n = 0; n < netlist.MenuN; n++) {
00341                 net = &netlist.Menu[n];
00342 
00343                 /* Weird, but correct */
00344                 fprintf(out, "net %s {\n", &net->Name[2]);
00345 
00346                 fprintf(out, "\tobjects = {\n");
00347 
00348                 for (m = 0; m < net->EntryN; m++) {
00349                         pin = &net->Entry[m];
00350 
00351                         /* pin_name_to_xy(pin, &x, &y); */
00352 
00353                         for (i = 0; i < MAX_GROUP; i++)
00354                                 if (nelma_export_group[i]) {
00355                                         idx = (i >= 0 && i < max_group) ?
00356                                                 PCB->LayerGroups.Entries[i][0] : i;
00357                                         ext = layer_type_to_file_name(idx, FNS_fixed);
00358 
00359                                         if (m != 0 || i != 0)
00360                                                 fprintf(out, ",\n");
00361                                         fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry,
00362                                                 ext);
00363                                 }
00364                 }
00365 
00366                 fprintf(out, "\n");
00367                 fprintf(out, "\t}\n");
00368                 fprintf(out, "}\n");
00369         }
00370 }
00371 
00372 static void 
00373 nelma_write_layer(FILE * out, int z, int h,
00374                   const char *name, int full,
00375                   char *mat)
00376 {
00377         LibraryType     netlist;
00378         LibraryMenuType *net;
00379         LibraryEntryType *pin;
00380 
00381         int             n, m;
00382 
00383         fprintf(out, "layer %s {\n", name);
00384         fprintf(out, "\theight = %d\n", h);
00385         fprintf(out, "\tz-order = %d\n", z);
00386         fprintf(out, "\tmaterial = \"%s\"\n", mat);
00387 
00388         if (full) {
00389                 fprintf(out, "\tobjects = {\n");
00390                 netlist = PCB->NetlistLib;
00391 
00392                 for (n = 0; n < netlist.MenuN; n++) {
00393                         net = &netlist.Menu[n];
00394 
00395                         for (m = 0; m < net->EntryN; m++) {
00396                                 pin = &net->Entry[m];
00397 
00398                                 if (m != 0 || n != 0)
00399                                         fprintf(out, ",\n");
00400                                 fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry,
00401                                         name);
00402                         }
00403 
00404                 }
00405                 fprintf(out, "\n\t}\n");
00406         }
00407         fprintf(out, "}\n");
00408 }
00409 
00410 static void 
00411 nelma_write_layers(FILE * out)
00412 {
00413         int             i, idx;
00414         int             z;
00415 
00416         const char     *ext;
00417         char            buf[100];
00418 
00419         int             subh;
00420 
00421         subh = nelma_substrateh / nelma_copperh;
00422 
00423         fprintf(out, "\n/* **** Layers **** */\n\n");
00424 
00425         /* Air layers on top and bottom of the stack */
00426         /* Their height is double substrate height. */
00427         nelma_write_layer(out, 1, 2 * subh, "air-top", 0, "air");
00428         nelma_write_layer(out, 1000, 2 * subh, "air-bottom", 0, "air");
00429 
00430         z = 10;
00431         for (i = 0; i < MAX_GROUP; i++)
00432                 if (nelma_export_group[i]) {
00433                         idx = (i >= 0 && i < max_group) ?
00434                                 PCB->LayerGroups.Entries[i][0] : i;
00435                         ext = layer_type_to_file_name(idx, FNS_fixed);
00436 
00437                         if (z != 10) {
00438                                 sprintf(buf, "substrate-%d", z);
00439                                 nelma_write_layer(out,
00440                                                   z,
00441                                                   subh,
00442                                                   buf,
00443                                                   0,
00444                                                   "composite");
00445                                 z++;
00446                         }
00447                         /*
00448                          * FIXME: for layers that are not on top or bottom,
00449                          * the material should be "composite"
00450                          */
00451                         nelma_write_layer(out, z, 1, ext, 1, "air");
00452 
00453                         z++;
00454                 }
00455 }
00456 
00457 static void 
00458 nelma_write_object(FILE * out, LibraryEntryType *pin)
00459 {
00460         int             i, idx;
00461         Coord           px = 0, py = 0;
00462         int             x, y;
00463 
00464         char           *f;
00465         const char     *ext;
00466 
00467         pin_name_to_xy (pin, &px, &py);
00468 
00469         x = pcb_to_nelma (px);
00470         y = pcb_to_nelma (py);
00471 
00472         for (i = 0; i < MAX_GROUP; i++)
00473                 if (nelma_export_group[i]) {
00474                         idx = (i >= 0 && i < max_group) ?
00475                                 PCB->LayerGroups.Entries[i][0] : i;
00476                         ext = layer_type_to_file_name(idx, FNS_fixed);
00477 
00478                         fprintf(out, "object %s-%s {\n", pin->ListEntry, ext);
00479                         fprintf(out, "\tposition = { 0, 0 }\n");
00480                         fprintf(out, "\tmaterial = \"copper\"\n");
00481                         fprintf(out, "\ttype = \"image\"\n");
00482                         fprintf(out, "\trole = \"net\"\n");
00483 
00484                         f = nelma_get_png_name(nelma_basename, ext);
00485 
00486                         fprintf(out, "\tfile = \"%s\"\n", f);
00487 
00488                         free(f);
00489 
00490                         fprintf(out, "\tfile-pos = { %d, %d }\n", x, y);
00491                         fprintf(out, "}\n");
00492                 }
00493 }
00494 
00495 static void 
00496 nelma_write_objects(FILE * out)
00497 {
00498         LibraryType     netlist;
00499         LibraryMenuType *net;
00500         LibraryEntryType *pin;
00501 
00502         int             n, m;
00503 
00504         netlist = PCB->NetlistLib;
00505 
00506         fprintf(out, "\n/* **** Objects **** */\n\n");
00507 
00508         for (n = 0; n < netlist.MenuN; n++) {
00509                 net = &netlist.Menu[n];
00510 
00511                 for (m = 0; m < net->EntryN; m++) {
00512                         pin = &net->Entry[m];
00513 
00514                         nelma_write_object(out, pin);
00515                 }
00516         }
00517 }
00518 
00519 /* *** Main export callback ************************************************ */
00520 
00521 static void 
00522 nelma_parse_arguments(int *argc, char ***argv)
00523 {
00524         hid_register_attributes(nelma_attribute_list,
00525                                 sizeof(nelma_attribute_list) /
00526                                 sizeof(nelma_attribute_list[0]));
00527         hid_parse_command_line(argc, argv);
00528 }
00529 
00530 static HID_Attribute *
00531 nelma_get_export_options(int *n)
00532 {
00533         static char    *last_made_filename = 0;
00534 
00535         if (PCB) {
00536                 derive_default_filename(PCB->Filename,
00537                                         &nelma_attribute_list[HA_basename],
00538                                         ".nelma",
00539                                         &last_made_filename);
00540         }
00541         if (n) {
00542                 *n = NUM_OPTIONS;
00543         }
00544         return nelma_attribute_list;
00545 }
00546 
00547 /* Populates nelma_export_group array */
00548 void 
00549 nelma_choose_groups()
00550 {
00551         int             n, m;
00552         LayerType      *layer;
00553 
00554         /* Set entire array to 0 (don't export any layer groups by default */
00555         memset(nelma_export_group, 0, sizeof(nelma_export_group));
00556 
00557         for (n = 0; n < max_copper_layer; n++) {
00558                 layer = &PCB->Data->Layer[n];
00559 
00560                 if (layer->LineN || layer->TextN || layer->ArcN ||
00561                     layer->PolygonN) {
00562                         /* layer isn't empty */
00563 
00564                         /*
00565                          * is this check necessary? It seems that special
00566                          * layers have negative indexes?
00567                          */
00568 
00569                         if (SL_TYPE(n) == 0) {
00570                                 /* layer is a copper layer */
00571                                 m = GetLayerGroupNumberByNumber(n);
00572 
00573                                 /* the export layer */
00574                                 nelma_export_group[m] = 1;
00575                         }
00576                 }
00577         }
00578 }
00579 
00580 static void 
00581 nelma_alloc_colors()
00582 {
00583         /*
00584          * Allocate white and black -- the first color allocated becomes the
00585          * background color
00586          */
00587 
00588         white = (struct color_struct *) malloc(sizeof(*white));
00589         white->r = white->g = white->b = 255;
00590         white->c = gdImageColorAllocate(nelma_im, white->r, white->g, white->b);
00591 
00592         black = (struct color_struct *) malloc(sizeof(*black));
00593         black->r = black->g = black->b = 0;
00594         black->c = gdImageColorAllocate(nelma_im, black->r, black->g, black->b);
00595 }
00596 
00597 static void 
00598 nelma_start_png(const char *basename, const char *suffix)
00599 {
00600         int             h, w;
00601         char           *buf;
00602 
00603         buf = nelma_get_png_name(basename, suffix);
00604 
00605         h = pcb_to_nelma(PCB->MaxHeight);
00606         w = pcb_to_nelma(PCB->MaxWidth);
00607 
00608         /* nelma_im = gdImageCreate (w, h); */
00609 
00610         /* Nelma only works with true color images */
00611         nelma_im = gdImageCreate(w, h);
00612         nelma_f = fopen(buf, "wb");
00613 
00614         nelma_alloc_colors();
00615 
00616         free(buf);
00617 }
00618 
00619 static void 
00620 nelma_finish_png()
00621 {
00622 #ifdef HAVE_GDIMAGEPNG
00623         gdImagePng(nelma_im, nelma_f);
00624 #else
00625         Message("NELMA: PNG not supported by gd. Can't write layer mask.\n");
00626 #endif
00627         gdImageDestroy(nelma_im);
00628         fclose(nelma_f);
00629 
00630         free(white);
00631         free(black);
00632 
00633         nelma_im = NULL;
00634         nelma_f = NULL;
00635 }
00636 
00637 void 
00638 nelma_start_png_export()
00639 {
00640         BoxType         region;
00641 
00642         region.X1 = 0;
00643         region.Y1 = 0;
00644         region.X2 = PCB->MaxWidth;
00645         region.Y2 = PCB->MaxHeight;
00646 
00647         linewidth = -1;
00648         lastbrush = (gdImagePtr)((void *) -1);
00649 
00650         hid_expose_callback(&nelma_hid, &region, 0);
00651 }
00652 
00653 static void 
00654 nelma_do_export(HID_Attr_Val * options)
00655 {
00656         int             save_ons[MAX_ALL_LAYER];
00657         int             i, idx;
00658         FILE           *nelma_config;
00659         char           *buf;
00660         int             len;
00661 
00662         time_t          t;
00663 
00664         if (!options) {
00665                 nelma_get_export_options(0);
00666                 for (i = 0; i < NUM_OPTIONS; i++) {
00667                         nelma_values[i] = nelma_attribute_list[i].default_val;
00668                 }
00669                 options = nelma_values;
00670         }
00671         nelma_basename = options[HA_basename].str_value;
00672         if (!nelma_basename) {
00673                 nelma_basename = "pcb-out";
00674         }
00675         nelma_dpi = options[HA_dpi].int_value;
00676         if (nelma_dpi < 0) {
00677                 fprintf(stderr, "ERROR:  dpi may not be < 0\n");
00678                 return;
00679         }
00680         nelma_copperh = options[HA_copperh].int_value;
00681         nelma_substrateh = options[HA_substrateh].int_value;
00682         nelma_substratee = options[HA_substratee].real_value;
00683 
00684         nelma_choose_groups();
00685 
00686         for (i = 0; i < MAX_GROUP; i++) {
00687                 if (nelma_export_group[i]) {
00688 
00689                         nelma_cur_group = i;
00690 
00691                         /* magic */
00692                         idx = (i >= 0 && i < max_group) ?
00693                                 PCB->LayerGroups.Entries[i][0] : i;
00694 
00695                         nelma_start_png(nelma_basename,
00696                                         layer_type_to_file_name(idx, FNS_fixed));
00697 
00698                         hid_save_and_show_layer_ons(save_ons);
00699                         nelma_start_png_export();
00700                         hid_restore_layer_ons(save_ons);
00701 
00702                         nelma_finish_png();
00703                 }
00704         }
00705 
00706         len = strlen(nelma_basename) + 4;
00707         buf = (char *)malloc(sizeof(*buf) * len);
00708 
00709         sprintf(buf, "%s.em", nelma_basename);
00710         nelma_config = fopen(buf, "w");
00711 
00712         free(buf);
00713 
00714         fprintf(nelma_config, "/* Made with PCB Nelma export HID */");
00715         t = time(NULL);
00716         fprintf(nelma_config, "/* %s */", ctime(&t));
00717 
00718         nelma_write_nets(nelma_config);
00719         nelma_write_objects(nelma_config);
00720         nelma_write_layers(nelma_config);
00721         nelma_write_materials(nelma_config);
00722         nelma_write_space(nelma_config);
00723 
00724         fclose(nelma_config);
00725 }
00726 
00727 /* *** PNG export (slightly modified code from PNG export HID) ************* */
00728 
00729 static int 
00730 nelma_set_layer(const char *name, int group, int empty)
00731 {
00732         int             idx = (group >= 0 && group < max_group) ?
00733         PCB->LayerGroups.Entries[group][0] : group;
00734 
00735         if (name == 0) {
00736                 name = PCB->Data->Layer[idx].Name;
00737         }
00738         if (strcmp(name, "invisible") == 0) {
00739                 return 0;
00740         }
00741         is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL);
00742         is_mask = (SL_TYPE(idx) == SL_MASK);
00743 
00744         if (is_mask) {
00745                 /* Don't print masks */
00746                 return 0;
00747         }
00748         if (is_drill) {
00749                 /*
00750                  * Print 'holes', so that we can fill gaps in the copper
00751                  * layer
00752                  */
00753                 return 1;
00754         }
00755         if (group == nelma_cur_group) {
00756                 return 1;
00757         }
00758         return 0;
00759 }
00760 
00761 static hidGC 
00762 nelma_make_gc(void)
00763 {
00764         hidGC           rv = (hidGC) malloc(sizeof(struct hid_gc_struct));
00765         rv->me_pointer = &nelma_hid;
00766         rv->cap = Trace_Cap;
00767         rv->width = 1;
00768         rv->color = (struct color_struct *) malloc(sizeof(*rv->color));
00769         rv->color->r = rv->color->g = rv->color->b = 0;
00770         rv->color->c = 0;
00771         return rv;
00772 }
00773 
00774 static void 
00775 nelma_destroy_gc(hidGC gc)
00776 {
00777         free(gc);
00778 }
00779 
00780 static void 
00781 nelma_use_mask(enum mask_mode mode)
00782 {
00783         /* does nothing */
00784 }
00785 
00786 static void 
00787 nelma_set_color(hidGC gc, const char *name)
00788 {
00789         if (nelma_im == NULL) {
00790                 return;
00791         }
00792         if (name == NULL) {
00793                 name = "#ff0000";
00794         }
00795         if (!strcmp(name, "drill")) {
00796                 gc->color = black;
00797                 gc->erase = 0;
00798                 return;
00799         }
00800         if (!strcmp(name, "erase")) {
00801                 /* FIXME -- should be background, not white */
00802                 gc->color = white;
00803                 gc->erase = 1;
00804                 return;
00805         }
00806         gc->color = black;
00807         gc->erase = 0;
00808         return;
00809 }
00810 
00811 static void
00812 nelma_set_line_cap(hidGC gc, EndCapStyle style)
00813 {
00814         gc->cap = style;
00815 }
00816 
00817 static void
00818 nelma_set_line_width(hidGC gc, Coord width)
00819 {
00820         gc->width = width;
00821 }
00822 
00823 static void
00824 nelma_set_draw_xor(hidGC gc, int xor_)
00825 {
00826         ;
00827 }
00828 
00829 static void
00830 nelma_set_draw_faded(hidGC gc, int faded)
00831 {
00832 }
00833 
00834 static void
00835 use_gc(hidGC gc)
00836 {
00837         int             need_brush = 0;
00838 
00839         if (gc->me_pointer != &nelma_hid) {
00840                 fprintf(stderr, "Fatal: GC from another HID passed to nelma HID\n");
00841                 abort();
00842         }
00843         if (linewidth != gc->width) {
00844                 /* Make sure the scaling doesn't erase lines completely */
00845                 /*
00846                 if (SCALE (gc->width) == 0 && gc->width > 0)
00847                   gdImageSetThickness (im, 1);
00848                 else
00849                 */
00850                 gdImageSetThickness(nelma_im, pcb_to_nelma(gc->width));
00851                 linewidth = gc->width;
00852                 need_brush = 1;
00853         }
00854         if (lastbrush != gc->brush || need_brush) {
00855                 static void    *bcache = 0;
00856                 hidval          bval;
00857                 char            name[256];
00858                 char            type;
00859                 int             r;
00860 
00861                 switch (gc->cap) {
00862                 case Round_Cap:
00863                 case Trace_Cap:
00864                         type = 'C';
00865                         r = pcb_to_nelma(gc->width / 2);
00866                         break;
00867                 default:
00868                 case Square_Cap:
00869                         r = pcb_to_nelma(gc->width);
00870                         type = 'S';
00871                         break;
00872                 }
00873                 sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g,
00874                         gc->color->b, type, r);
00875 
00876                 if (hid_cache_color(0, name, &bval, &bcache)) {
00877                   gc->brush = (gdImagePtr)bval.ptr;
00878                 } else {
00879                         int             bg, fg;
00880                         if (type == 'C')
00881                                 gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1);
00882                         else
00883                                 gc->brush = gdImageCreate(r + 1, r + 1);
00884                         bg = gdImageColorAllocate(gc->brush, 255, 255, 255);
00885                         fg =
00886                                 gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g,
00887                                                      gc->color->b);
00888                         gdImageColorTransparent(gc->brush, bg);
00889 
00890                         /*
00891                          * if we shrunk to a radius/box width of zero, then just use
00892                          * a single pixel to draw with.
00893                          */
00894                         if (r == 0)
00895                                 gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg);
00896                         else {
00897                                 if (type == 'C')
00898                                         gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg);
00899                                 else
00900                                         gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg);
00901                         }
00902                         bval.ptr = gc->brush;
00903                         hid_cache_color(1, name, &bval, &bcache);
00904                 }
00905 
00906                 gdImageSetBrush(nelma_im, gc->brush);
00907                 lastbrush = gc->brush;
00908 
00909         }
00910 }
00911 
00912 static void
00913 nelma_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
00914 {
00915         use_gc(gc);
00916         gdImageRectangle(nelma_im,
00917                          pcb_to_nelma(x1), pcb_to_nelma(y1),
00918                          pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c);
00919 }
00920 
00921 static void
00922 nelma_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
00923 {
00924         use_gc(gc);
00925         gdImageSetThickness(nelma_im, 0);
00926         linewidth = 0;
00927         gdImageFilledRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1),
00928                           pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c);
00929 }
00930 
00931 static void
00932 nelma_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
00933 {
00934         if (x1 == x2 && y1 == y2) {
00935                 Coord             w = gc->width / 2;
00936                 nelma_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w);
00937                 return;
00938         }
00939         use_gc(gc);
00940 
00941         gdImageSetThickness(nelma_im, 0);
00942         linewidth = 0;
00943         gdImageLine(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1),
00944                     pcb_to_nelma(x2), pcb_to_nelma(y2), gdBrushed);
00945 }
00946 
00947 static void
00948 nelma_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
00949                Angle start_angle, Angle delta_angle)
00950 {
00951         Angle sa, ea;
00952 
00953         /*
00954          * in gdImageArc, 0 degrees is to the right and +90 degrees is down
00955          * in pcb, 0 degrees is to the left and +90 degrees is down
00956          */
00957         start_angle = 180 - start_angle;
00958         delta_angle = -delta_angle;
00959         if (delta_angle > 0) {
00960                 sa = start_angle;
00961                 ea = start_angle + delta_angle;
00962         } else {
00963                 sa = start_angle + delta_angle;
00964                 ea = start_angle;
00965         }
00966 
00967         /*
00968          * make sure we start between 0 and 360 otherwise gd does strange
00969          * things
00970          */
00971         sa = NormalizeAngle (sa);
00972         ea = NormalizeAngle (ea);
00973 
00974 #if 0
00975         printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
00976                cx, cy, width, height, start_angle, delta_angle, sa, ea);
00977         printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
00978                im, SCALE_X(cx), SCALE_Y(cy),
00979                SCALE(width), SCALE(height), sa, ea, gc->color->c);
00980 #endif
00981         use_gc(gc);
00982         gdImageSetThickness(nelma_im, 0);
00983         linewidth = 0;
00984         gdImageArc(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy),
00985                    pcb_to_nelma(2 * width), pcb_to_nelma(2 * height), sa, ea, gdBrushed);
00986 }
00987 
00988 static void
00989 nelma_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius)
00990 {
00991         use_gc(gc);
00992 
00993         gdImageSetThickness(nelma_im, 0);
00994         linewidth = 0;
00995         gdImageFilledEllipse(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy),
00996           pcb_to_nelma(2 * radius), pcb_to_nelma(2 * radius), gc->color->c);
00997 
00998 }
00999 
01000 static void
01001 nelma_fill_polygon(hidGC gc, int n_coords, Coord *x, Coord *y)
01002 {
01003         int             i;
01004         gdPoint        *points;
01005 
01006         points = (gdPoint *) malloc(n_coords * sizeof(gdPoint));
01007         if (points == NULL) {
01008                 fprintf(stderr, "ERROR:  nelma_fill_polygon():  malloc failed\n");
01009                 exit(1);
01010         }
01011         use_gc(gc);
01012         for (i = 0; i < n_coords; i++) {
01013                 points[i].x = pcb_to_nelma(x[i]);
01014                 points[i].y = pcb_to_nelma(y[i]);
01015         }
01016         gdImageSetThickness(nelma_im, 0);
01017         linewidth = 0;
01018         gdImageFilledPolygon(nelma_im, points, n_coords, gc->color->c);
01019         free(points);
01020 }
01021 
01022 static void
01023 nelma_calibrate(double xval, double yval)
01024 {
01025         CRASH;
01026 }
01027 
01028 static void
01029 nelma_set_crosshair(int x, int y, int a)
01030 {
01031 }
01032 
01033 /* *** Miscellaneous ******************************************************* */
01034 
01035 #include "dolists.h"
01036 
01037 void
01038 hid_nelma_init()
01039 {
01040   memset (&nelma_hid, 0, sizeof (HID));
01041   memset (&nelma_graphics, 0, sizeof (HID_DRAW));
01042 
01043   common_nogui_init (&nelma_hid);
01044   common_draw_helpers_init (&nelma_graphics);
01045 
01046   nelma_hid.struct_size         = sizeof (HID);
01047   nelma_hid.name                = "nelma";
01048   nelma_hid.description         = "Numerical analysis package export";
01049   nelma_hid.exporter            = 1;
01050   nelma_hid.poly_before         = 1;
01051 
01052   nelma_hid.get_export_options  = nelma_get_export_options;
01053   nelma_hid.do_export           = nelma_do_export;
01054   nelma_hid.parse_arguments     = nelma_parse_arguments;
01055   nelma_hid.set_layer           = nelma_set_layer;
01056   nelma_hid.calibrate           = nelma_calibrate;
01057   nelma_hid.set_crosshair       = nelma_set_crosshair;
01058 
01059   nelma_hid.graphics            = &nelma_graphics;
01060 
01061   nelma_graphics.make_gc        = nelma_make_gc;
01062   nelma_graphics.destroy_gc     = nelma_destroy_gc;
01063   nelma_graphics.use_mask       = nelma_use_mask;
01064   nelma_graphics.set_color      = nelma_set_color;
01065   nelma_graphics.set_line_cap   = nelma_set_line_cap;
01066   nelma_graphics.set_line_width = nelma_set_line_width;
01067   nelma_graphics.set_draw_xor   = nelma_set_draw_xor;
01068   nelma_graphics.set_draw_faded = nelma_set_draw_faded;
01069   nelma_graphics.draw_line      = nelma_draw_line;
01070   nelma_graphics.draw_arc       = nelma_draw_arc;
01071   nelma_graphics.draw_rect      = nelma_draw_rect;
01072   nelma_graphics.fill_circle    = nelma_fill_circle;
01073   nelma_graphics.fill_polygon   = nelma_fill_polygon;
01074   nelma_graphics.fill_rect      = nelma_fill_rect;
01075 
01076   hid_register_hid (&nelma_hid);
01077 
01078 #include "nelma_lists.h"
01079 }