diff --git a/src/hid/gerber/gerber.c b/src/hid/gerber/gerber.c index f03b3d8..62532f1 100644 --- a/src/hid/gerber/gerber.c +++ b/src/hid/gerber/gerber.c @@ -1,5 +1,24 @@ /* $Id: gerber.c,v 1.37 2008/12/27 16:57:46 djdelorie Exp $ */ +/* 19 Feb 2007 (updated Oct 2008, Jan 2009) Ineiev: + more features: + (1) metric output (separately in Gerbers and Excellons) + (2) 0..6 precision both in metric Excellon output + and (separately for X and Y axes) in Gerber + (3) decimal point in Excellon output + (4) outline every Gerber + (5) don't round drill diameters + (6) digits before the decimal point are autodetected + (e.g. specify 3.x format when the maximum coordinates are + less than 1000 and more than 100) + + reference URLs: + Excellon format description + http://www.excellon.com/manuals/button.htm + http://www.excellon.com/manuals/program.htm + Gerber + http://www.artwork.com/gerber/274x/rs274xrevd_e.pdf +*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -25,6 +44,7 @@ #include "data.h" #include "misc.h" #include "error.h" +#include "draw.h" #include "hid.h" #include "../hidint.h" @@ -45,29 +65,189 @@ static void gerber_fill_polygon (hidGC gc, int n_coords, int *x, int *y); /* Utility routines */ /*----------------------------------------------------------------------------*/ -/* These are for films */ -#define gerberX(pcb, x) ((long) ((x))) -#define gerberY(pcb, y) ((long) (((pcb)->MaxHeight - (y)))) -#define gerberXOffset(pcb, x) ((long) ((x))) -#define gerberYOffset(pcb, y) ((long) (-(y))) +/* scale factor for Gerber coordinates */ +static double gerber_x_scale, gerber_y_scale; +/* powers of 10 more than maximum absolute values of the coordinates + * output into gerbers to determine format */ +static int gerber_max_x = 100, gerber_max_y = 100; +/* 10^gerber_x_precision and 10^gerber_y_precision */ +static int gerber_x_prescaler = 1, gerber_y_prescaler = 1; +/* the same for Excellon files */ +static int drill_max_x = 1; +static int drill_prescaler = 1; +/* digits before decimal point in Gerber files */ +static int gerber_x_digits = 2, gerber_y_digits = 2; +/* the same for Excellon files */ +static int drill_digits = 0; +/* whether we are running first pass */ +static int finding_apertures = 0; + +static void +check_max_x (double x) +{ + if(!finding_apertures) + return; + x /= gerber_x_prescaler; + while (x >= gerber_max_x || -x >= gerber_max_x) + { + gerber_max_x *= 10; + gerber_x_digits++; + } +} + +static void +check_max_y (double y) +{ + if(!finding_apertures) + return; + y /= gerber_y_prescaler; + while (y >= gerber_max_y || -y >= gerber_max_y) + { + gerber_max_y *= 10; + gerber_y_digits++; + } +} + +static void +check_max_drill (double x) +{ + if(!finding_apertures) + return; + x /= drill_prescaler; + while (x >= drill_max_x || -x >= drill_max_x) + { + drill_max_x *= 10; + drill_digits++; + } +} + +static double +gerberX (const PCBTypePtr pcb, int x) +{ + double val = x / gerber_x_scale; + (void) pcb; /* suppress GCC warning on unused argument */ + check_max_x (val); + return val; +} -/* These are for drills */ -#define gerberDrX(pcb, x) ((long) ((x)/10)) -#define gerberDrY(pcb, y) ((long) (((pcb)->MaxHeight - (y)))/10) -#define gerberDrXOffset(pcb, x) ((long) ((x)/10)) -#define gerberDrYOffset(pcb, y) ((long) (-(y))/10) +static double +gerberY (const PCBTypePtr pcb, int y) +{ + double val = (pcb->MaxHeight - (y)) / gerber_y_scale; + check_max_y (val); + return val; +} + +static double +gerberXOffset (const PCBTypePtr pcb, int x) +{ + double val = x / gerber_x_scale; + (void) pcb; + check_max_x (val); + return val; +} + +static double +gerberYOffset (const PCBTypePtr pcb, int y) +{ + double val = -y / gerber_y_scale; + (void) pcb; + check_max_y (val); + return val; +} +/* scale factor for Excellon coordinates */ +static double drill_scale; + +static double +drillX (const PCBTypePtr pcb, int x) +{ + double val = x / drill_scale; + (void) pcb; + check_max_drill (val); + return val; +} + +static double +drillY (const PCBTypePtr pcb, int y) +{ + double val = (pcb->MaxHeight - y) / drill_scale; + check_max_drill (val); + return val; +} /*----------------------------------------------------------------------------*/ /* Private data structures */ /*----------------------------------------------------------------------------*/ static int verbose; +/* export all layers including empty layers */ static int all_layers; +/* whether to add the board outline to every Gerber */ +static int outline_gerbers; +/* whether to produce metric Gerbers instead of imperial ones */ +static int gerber_mm_units; +/* gerber_precision is number of digits after decimal point + in Gerber coordinates output */ +static int gerber_x_precision = 5, gerber_y_precision = 5; +/* whether to produce metric Excellons instead of imperial ones */ +static int drill_mm_units; +/* whether to use decimal point in coordinates in Excellons */ +static int float_drills; +/* whether to pass drill diameters without rounding */ +static int raw_drill_diameters; +/* drill_precision is number of digits after decimal point + in Excellon coordinates output */ +static int drill_precision, specified_drill_precision; static int is_mask, was_drill; static int is_drill; static int current_mask; static int flash_drills; +static LayerTypePtr outline_layer; + +/* set gerber_scale according to selected options */ +static void +set_gerber_scale (double *gerber_scale, int *gerber_precision, + int val, int min_val, int max_val) +{ + *gerber_precision = MAX (val, min_val); + *gerber_precision = MIN (*gerber_precision, max_val); + + if (gerber_mm_units) + *gerber_scale = MM_TO_COOR / 10000.; + else + *gerber_scale = 10; + *gerber_scale *= pow (10, 4 - *gerber_precision); +} + +/* correct drill_precision according to selected options */ +static void +set_drill_precision (void) +{ + if (drill_precision >=0) /* drill precision is user-specified */ + return; + /* set default precision */ + drill_precision = drill_mm_units? 3: 4; +} + +/* set drill_scale according to selected options */ +static void +set_drill_scale (void) +{ + if (float_drills) + { + if (drill_mm_units) + drill_scale = MM_TO_COOR; + else + drill_scale = 1e5; + return; + } + if (drill_mm_units) + drill_scale = MM_TO_COOR / 10000.; + else + drill_scale = 10; + drill_scale *= pow (10, 4 - drill_precision); +} enum ApertureShape { ROUND, /* Shaped like a circle */ @@ -100,9 +280,18 @@ typedef struct static int global_aperture_count; static int global_aperture_sizes[GBX_MAXAPERTURECOUNT]; static ApertureShape global_aperture_shapes[GBX_MAXAPERTURECOUNT]; +/* the aperture for fake output in empty layers + when --all-layers is requested*/ +static int largest_aperture_width; +static ApertureShape largest_aperture_shape; +/* when --all-layers is requested and there are no holes, + we shall output empty Excellon files */ +static int plated_is_empty, unplated_is_empty; static Apertures *layerapps = 0; static Apertures *curapp; +static int outline_layer_num;/*index of outline layer apertures in layerapps[]*/ +static int fab_layer_num;/*index of fab layer apertures in layerapps[]*/ static int n_layerapps = 0; static int c_layerapps = 0; @@ -130,6 +319,11 @@ findApertureCode (int width, ApertureShape shape) if (width == 0) return (0); + if (finding_apertures && largest_aperture_width < width) + { + largest_aperture_width = width; + largest_aperture_shape = shape; + } /* Search for an appropriate aperture. */ for (i = 0; i < global_aperture_count; i++) @@ -165,37 +359,41 @@ printAperture(FILE *f, int i) { int dCode = i + DCODE_BASE; int width = global_aperture_sizes[i]; + double scale = 100000.0; + + if (gerber_mm_units) + scale /= 1000 * MIL_TO_MM; switch (global_aperture_shapes[i]) { case ROUND: fprintf (f, "%%ADD%dC,%.4f*%%\015\012", dCode, - width / 100000.0); + width / scale); break; case SQUARE: fprintf (f, "%%ADD%dR,%.4fX%.4f*%%\015\012", - dCode, width / 100000.0, width / 100000.0); + dCode, width / scale, width / scale); break; case OCTAGON: fprintf (f, "%%AMOCT%d*5,0,8,0,0,%.4f,22.5*%%\015\012" "%%ADD%dOCT%d*%%\015\012", dCode, - width / (100000.0 * COS_22_5_DEGREE), dCode, + width / (scale * COS_22_5_DEGREE), dCode, dCode); break; #if 0 case THERMAL: fprintf (f, "%%AMTHERM%d*7,0,0,%.4f,%.4f,%.4f,45*%%\015\012" - "%%ADD%dTHERM%d*%%\015\012", dCode, gap / 100000.0, - width / 100000.0, finger / 100000.0, dCode, dCode); + "%%ADD%dTHERM%d*%%\015\012", dCode, gap / scale, + width / scale, finger / scale, dCode, dCode); break; case ROUNDCLEAR: fprintf (f, "%%ADD%dC,%.4fX%.4f*%%\015\012", - dCode, gap / 100000.0, width / 100000.0); + dCode, gap / scale, width / scale); break; case SQUARECLEAR: fprintf (f, "%%ADD%dR,%.4fX%.4fX%.4fX%.4f*%%\015\012", - dCode, gap / 100000.0, gap / 100000.0, - width / 100000.0, width / 100000.0); + dCode, gap / scale, gap / scale, + width / scale, width / scale); break; #else default: @@ -261,7 +459,6 @@ static char *filesuff; static char *layername = 0; static int lncount = 0; -static int finding_apertures = 0; static int pagecount = 0; static int linewidth = -1; static int lastgroup = -1; @@ -274,24 +471,54 @@ static int lastX, lastY; /* the last X and Y coordinate */ static HID_Attribute gerber_options[] = { {"gerberfile", "Gerber output file base", HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_gerberfile 0 {"all-layers", "Print all layers, even empty ones", HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_all_layers 1 {"verbose", "print file names and aperture counts", HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_verbose 2 + {"outline-gerbers", "add board outline to every Gerber file", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, + {"metric-gerbers", "use mm units in Gerber files", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, + {"gerber-x-precision", + "length of the fractional part of Gerber X coordinates", + HID_Integer, 0, 6, {5, 0, 0}, 0, 0}, + {"gerber-y-precision", + "length of the fractional part of Gerber Y coordinates", + HID_Integer, 0, 6, {5, 0, 0}, 0, 0}, + {"metric-drills", "use mm units in Excellon files", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, + {"drill-precision", + "digits after decimal point in drill files (-1 for default)", + HID_Integer, -1, 6, {-1, 0, 0}, 0, 0}, + {"decimal-in-drills", "use decimal point in Excellon files", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, + {"raw-drill-diameters", "don't normalise drill diameters", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, }; #define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0])) +static int +find_option (const char *name) +{ + int i; + for (i = 0; i < NUM_OPTIONS; i++) + if (!strcmp (name, gerber_options[i].name)) + return i; + fprintf (stderr, _("error: unknown gerber exporter option `%s'\n"), name); + return 0; +} + static HID_Attr_Val gerber_values[NUM_OPTIONS]; static HID_Attribute * gerber_get_export_options (int *n) { static char *last_made_filename = 0; - if (PCB) derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename); + if (PCB) + derive_default_filename(PCB->Filename, + gerber_options + find_option ("gerberfile"), + "", &last_made_filename); if (n) *n = NUM_OPTIONS; @@ -333,6 +560,67 @@ maybe_close_f () } static void +print_drill_header (FILE *f) +{ + fprintf (f, "M48\015\012" "%s", + drill_mm_units? "METRIC": "INCH"); + /* don't emerge width specifications for imperial files + when the default 00.0000 fits for us */ + if (drill_digits < 2 && drill_precision == 4 && !drill_mm_units) + drill_digits = 6 - drill_precision; + /* specify precision for metric files without floating point + and for queerly scaled imperial files */ + /* the documented formats are 00.0000 (inch), + 000.000, 0000.00 and 000.00 (metric), + all the rest is not strictly compliant; however, + most of contemporary software shall not probably misundestand it */ + if (!float_drills && (drill_mm_units || drill_precision != 4 + || drill_precision + drill_digits != 6)) + { + int units = MAX (6 - drill_precision, drill_digits); + + /* make total digits number as close as possible to 6 + when drill precision is not specified explicitly */ + if (specified_drill_precision < 0 && units + drill_precision != 6) + { + int dp = drill_precision; + + drill_precision = MAX (6 - units, 0); + dp -= drill_precision; + if (dp) + drill_scale *= pow (10, dp); + } + fprintf (f, ",%*.*d.%*.*d", units, units, 0, + drill_precision, drill_precision, 0); + } + fprintf (f, "\015\012"); +} + +/* produce empty drill file for the case + when --all-layers is requested and there were no drills */ +static void +print_empty_drill (int plated) +{ + int idx = plated? SL (PDRILL, 0): SL (UDRILL,0); + strcpy (filesuff, layer_type_to_file_name (idx)); + strcat (filesuff, ".cnc"); + f = fopen (filename, "w"); + if (f == NULL) + { + Message ( "Error: Could not open %s for writing.\n", filename); + return; + } + print_drill_header(f); + /* print a drill set: unused, but non-empty */ + fprintf (f, "T01C%.3f\015\012", drill_mm_units? 1.: .04); + fprintf (f, "%%\015\012"); + /* close file */ + fprintf (f, "M30\015\012"); + fclose (f); + f = 0; +} + +static void gerber_do_export (HID_Attr_Val * options) { char *fnbase; @@ -354,12 +642,48 @@ gerber_do_export (HID_Attr_Val * options) options = gerber_values; } - fnbase = options[HA_gerberfile].str_value; + fnbase = options[find_option ("gerberfile")].str_value; if (!fnbase) fnbase = "pcb-out"; - verbose = options[HA_verbose].int_value; - all_layers = options[HA_all_layers].int_value; + outline_layer_num = fab_layer_num = -1; + + verbose = options[find_option ("verbose")].int_value; + all_layers = options[find_option ("all-layers")].int_value; + outline_gerbers = options[find_option ("outline-gerbers")].int_value; + + outline_layer = NULL; + if (outline_gerbers) + for (i = 0; i < max_layer; i++) + { + LayerType *layer = PCB->Data->Layer + i; + + if (strcmp (layer->Name, "outline") == 0 + || strcmp (layer->Name, "route") == 0) + outline_layer = layer; + } + + gerber_mm_units = options[find_option ("metric-gerbers")].int_value; + i = find_option ("gerber-x-precision"); + set_gerber_scale + (&gerber_x_scale, &gerber_x_precision, options[i].int_value, + gerber_options[i].min_val, gerber_options[i].max_val); + gerber_x_prescaler = pow (10, gerber_x_precision); + i = find_option ("gerber-y-precision"); + set_gerber_scale + (&gerber_y_scale, &gerber_y_precision, options[i].int_value, + gerber_options[i].min_val, gerber_options[i].max_val); + gerber_y_prescaler = pow (10, gerber_y_precision); + + drill_mm_units = options[find_option ("metric-drills")].int_value; + i = find_option ("drill-precision"); + drill_precision = MIN (options[i].int_value, gerber_options[i].max_val); + specified_drill_precision = drill_precision; + float_drills = options[find_option ("decimal-in-drills")].int_value; + raw_drill_diameters = options[find_option ("raw-drill-diameters")].int_value; + set_drill_precision (); + set_drill_scale (); + drill_prescaler = pow (10, drill_precision); i = strlen (fnbase); filename = MyRealloc (filename, i + 40, "gerber"); @@ -406,12 +730,21 @@ gerber_do_export (HID_Attr_Val * options) pagecount = 1; initApertures (); + largest_aperture_width = 0; + plated_is_empty = unplated_is_empty = all_layers; f = 0; lastgroup = -1; c_layerapps = 0; finding_apertures = 1; hid_expose_callback (&gerber_hid, ®ion, 0); + if (outline_layer_num < 0) + { + /* expand gerber_max_x and gerber_max_y */ + gerberX (PCB, 0); gerberX (PCB, PCB->MaxWidth); + gerberY (PCB, 0); gerberY (PCB, PCB->MaxHeight); + } + c_layerapps = 0; finding_apertures = 0; hid_expose_callback (&gerber_hid, ®ion, 0); @@ -419,6 +752,10 @@ gerber_do_export (HID_Attr_Val * options) memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack)); maybe_close_f (); + if (plated_is_empty) + print_empty_drill(!0); + if (unplated_is_empty) + print_empty_drill(0); hid_restore_layer_ons (save_ons); PCB->Flags = save_thindraw; } @@ -446,6 +783,52 @@ drill_sort (const void *va, const void *vb) return b->y - b->y; } +/* output outline into current Gerber file */ +static void +draw_outline(void) +{ + if (outline_layer_num < 0) + { + int i, width = 0, ac = -1; + ApertureShape as; + + if (fab_layer_num < 0) + { + fprintf (stderr, "no `fab' apertures found\n"); + return; + } + for (i = 0; i < GBX_MAXAPERTURECOUNT; i++) + if (layerapps[fab_layer_num].aperture_used[i]) + { + width = global_aperture_sizes[i]; + as = global_aperture_shapes[i]; + ac = findApertureCode (width, as); + break; + } + if (ac <= 0) + { + fprintf (stderr, "error: aperture for radius %d is %d\n", + width, ac); + return; + } + fprintf (f, "G54D%d*X%.0fY%.0fD02*", ac, gerberX (PCB, 0), gerberY (PCB, 0)); + fprintf (f, "X%.0fD01*\015\012", gerberX (PCB, PCB->MaxWidth)); + fprintf (f, "Y%.0fD01*\015\012", gerberY (PCB, PCB->MaxHeight)); + fprintf (f, "X%.0fD01*\015\012", gerberX (PCB, 0)); + fprintf (f, "Y%.0fD01*\015\012", gerberY (PCB, 0)); + } + else + { + BoxType region; + + region.X1 = 0; + region.Y1 = 0; + region.X2 = PCB->MaxWidth; + region.Y2 = PCB->MaxHeight; + DrawLayer (outline_layer, ®ion); + } +} + static int gerber_set_layer (const char *name, int group, int empty) { @@ -473,6 +856,14 @@ gerber_set_layer (const char *name, int group, int empty) if (is_drill && n_pending_drills) { int i; + char format[289]; + + if (float_drills) + sprintf (format, "X%%.%ifY%%.%if\015\012", + drill_precision, drill_precision); + else + sprintf (format, "X%%0%i.0fY%%0%i.0f\015\012", + drill_digits+drill_precision, drill_digits+drill_precision); /* dump pending drills in sequence */ qsort (pending_drills, n_pending_drills, sizeof (PendingDrills), drill_sort); @@ -483,9 +874,9 @@ gerber_set_layer (const char *name, int group, int empty) int ap = findApertureCode (pending_drills[i].diam, ROUND); fprintf (f, "T%02d\015\012", ap); } - fprintf (f, "X%06ldY%06ld\015\012", - gerberDrX (PCB, pending_drills[i].x), - gerberDrY (PCB, pending_drills[i].y)); + fprintf (f, format, + drillX (PCB, pending_drills[i].x), + drillY (PCB, pending_drills[i].y)); } free (pending_drills); n_pending_drills = max_pending_drills = 0; @@ -509,7 +900,6 @@ gerber_set_layer (const char *name, int group, int empty) #endif char *sext=".gbr"; int i; - int some_apertures = 0; lastgroup = group; lastX = -1; @@ -522,10 +912,22 @@ gerber_set_layer (const char *name, int group, int empty) c_layerapps++; if (finding_apertures) - return 1; + { + if ((strcmp (name, "outline") == 0 + || strcmp (name, "route") == 0)) + outline_layer_num = curapp - layerapps; + if (strcmp (name, "fab") == 0) + fab_layer_num = curapp - layerapps; + return 1; + } - if (!curapp->some_apertures && !all_layers) - return 0; + if (!curapp->some_apertures) + { + if (all_layers) + findApertureCode (largest_aperture_width, largest_aperture_shape); + else + return 0; + } maybe_close_f (); @@ -534,9 +936,11 @@ gerber_set_layer (const char *name, int group, int empty) { case SL (PDRILL, 0): sext = ".cnc"; + plated_is_empty = 0; break; case SL (UDRILL, 0): sext = ".cnc"; + unplated_is_empty = 0; break; } strcpy (filesuff, layer_type_to_file_name (idx)); @@ -553,22 +957,24 @@ gerber_set_layer (const char *name, int group, int empty) if (verbose) { int c = countApertures (curapp); - printf ("Gerber: %d aperture%s in %s\n", c, - c == 1 ? "" : "s", filename); + printf ("Gerber: apertures number in `%s' is %d\n", filename, c); } if (is_drill) { - /* We omit the ,TZ here because we are not omitting trailing zeros. Our format is - always six-digit 0.1 mil resolution (i.e. 001100 = 0.11")*/ - fprintf (f, "M48\015\012" "INCH\015\012"); + double scale = 100000.0; + + if (drill_mm_units) + scale /= 1000 * MIL_TO_MM; + + print_drill_header(f); + for (i = 0; i < GBX_MAXAPERTURECOUNT; i++) if (curapp->aperture_used[i]) fprintf (f, "T%02dC%.3f\015\012", i + DCODE_BASE, - global_aperture_sizes[i] / 100000.0); + global_aperture_sizes[i] / scale); fprintf (f, "%%\015\012"); - /* FIXME */ return 1; } @@ -599,11 +1005,14 @@ gerber_set_layer (const char *name, int group, int empty) PCB->MaxWidth, PCB->MaxHeight); fprintf (f, "G04 PCB-Coordinate-Origin: lower left *\015\012"); - /* Signal data in inches. */ - fprintf (f, "%%MOIN*%%\015\012"); + /* Signal data in selected units. */ + fprintf (f, "%%MO%s*%%\015\012", gerber_mm_units? "MM": "IN"); - /* Signal Leading zero suppression, Absolute Data, 2.5 format */ - fprintf (f, "%%FSLAX25Y25*%%\015\012"); + /* Signal Leading zero suppression, Absolute Data, + {detected limit}.{selected precision} format */ + fprintf (f, "%%FSLAX%i%iY%i%i*%%\015\012", + gerber_x_digits, gerber_x_precision, + gerber_y_digits, gerber_y_precision); /* build a legal identifier. */ if (layername) @@ -621,17 +1030,23 @@ gerber_set_layer (const char *name, int group, int empty) lncount = 1; for (i=0; iaperture_used[i]) - { - some_apertures ++; + if (curapp->aperture_used[i] + || (outline_gerbers && outline_layer_num >= 0 + && layerapps[outline_layer_num].aperture_used[i]) + || (outline_gerbers && outline_layer_num < 0 + && fab_layer_num >= 0 + && layerapps[fab_layer_num].aperture_used[i])) printAperture(f, i); - } - if (!some_apertures) - /* We need to put *something* in the file to make it be parsed - as RS-274X instead of RS-274D. */ - fprintf (f, "%%ADD11C,0.0100*%%\r\n"); } + if (!outline_gerbers) + return 1; + if (!(strcmp (name, "outline") && strcmp (name, "fab"))) + return 1; + + fprintf (f, "G04 The next is the board outline *\015\012"); + draw_outline (); + fprintf (f, "G04 The board outline complete *\015\012"); return 1; } @@ -801,6 +1216,7 @@ static void gerber_draw_line (hidGC gc, int x1, int y1, int x2, int y2) { Boolean m = False; + double coor; if (x1 != x2 && y1 != y2 && gc->cap == Square_Cap) { @@ -825,39 +1241,49 @@ gerber_draw_line (hidGC gc, int x1, int y1, int x2, int y2) } use_gc (gc, 0); - if (!f) - return; if (x1 != lastX) { m = True; lastX = x1; - fprintf (f, "X%ld", gerberX (PCB, lastX)); + coor = gerberX (PCB, lastX); + if (f) + fprintf (f, "X%.0f", coor); } if (y1 != lastY) { m = True; lastY = y1; - fprintf (f, "Y%ld", gerberY (PCB, lastY)); + coor = gerberY (PCB, lastY); + if (f) + fprintf (f, "Y%.0f", coor); } if ((x1 == x2) && (y1 == y2)) - fprintf (f, "D03*\015\012"); + { + if (f) + fprintf (f, "D03*\015\012"); + } else { - if (m) + if (m && f) fprintf (f, "D02*"); if (x2 != lastX) { lastX = x2; - fprintf (f, "X%ld", gerberX (PCB, lastX)); + coor = gerberX (PCB, lastX); + if (f) + fprintf (f, "X%.0f", coor); } if (y2 != lastY) { lastY = y2; - fprintf (f, "Y%ld", gerberY (PCB, lastY)); + coor = gerberY (PCB, lastY); + if (f) + fprintf (f, "Y%.0f", coor); } - fprintf (f, "D01*\015\012"); + if (f) + fprintf (f, "D01*\015\012"); } } @@ -868,13 +1294,12 @@ gerber_draw_arc (hidGC gc, int cx, int cy, int width, int height, { Boolean m = False; float arcStartX, arcStopX, arcStartY, arcStopY; + double x, y, ox, oy; if (gc->width == 1) return; use_gc (gc, 0); - if (!f) - return; arcStartX = cx - width * cos (TO_RADIANS (start_angle)); arcStartY = cy + height * sin (TO_RADIANS (start_angle)); @@ -922,27 +1347,58 @@ gerber_draw_arc (hidGC gc, int cx, int cy, int width, int height, { m = True; lastX = arcStartX; - fprintf (f, "X%ld", gerberX (PCB, lastX)); + x = gerberX (PCB, lastX); + if (f) + fprintf (f, "X%.0f", x); } if (arcStartY != lastY) { m = True; lastY = arcStartY; - fprintf (f, "Y%ld", gerberY (PCB, lastY)); + y = gerberY (PCB, lastY); + if (f) + fprintf (f, "Y%.0f", y); } - if (m) + if (m && f) fprintf (f, "D02*"); - fprintf (f, - "G75*G0%1dX%ldY%ldI%ldJ%ldD01*G01*\015\012", - (delta_angle < 0) ? 2 : 3, - gerberX (PCB, arcStopX), gerberY (PCB, arcStopY), - gerberXOffset (PCB, cx - arcStartX), - gerberYOffset (PCB, cy - arcStartY)); + x = gerberX (PCB, arcStopX); + y = gerberY (PCB, arcStopY); + ox = gerberXOffset (PCB, cx - arcStartX); + oy = gerberYOffset (PCB, cy - arcStartY); + if (f) + fprintf (f, + "G75*G0%1dX%.0fY%.0fI%.0fJ%.0fD01*G01*\015\012", + (delta_angle < 0) ? 2 : 3, x, y, ox, oy); lastX = arcStopX; lastY = arcStopY; } -#define ROUND(x) ((int)(((x)+50)/100)*100) +static int +normalised_drill (int radius) +{ + int diameter; + +/* actually there is no need for --raw-drill-diameters + in imperial mode, because tool diameter resolution is 1 mil, + and it may result just in multiple tools with the same diameter; + however, in metric files rounding step is 0.1 mm, so + this may be more meaningful */ + if (raw_drill_diameters) + return radius; + diameter = radius * 2; + if (drill_mm_units) + { + double mm_step = .1 / COOR_TO_MM; + diameter = floor (diameter/mm_step + .5) * mm_step; + } + else + { + int inch_step = 100; + diameter = (diameter + inch_step/2) / inch_step; + diameter *= inch_step; + } + return diameter / 2; +} static void gerber_fill_circle (hidGC gc, int cx, int cy, int radius) @@ -950,38 +1406,50 @@ gerber_fill_circle (hidGC gc, int cx, int cy, int radius) if (radius <= 0) return; if (is_drill) - radius = ROUND(radius*2)/2; + radius = normalised_drill(radius); use_gc (gc, radius); - if (!f) - return; if (is_drill) { - if (n_pending_drills >= max_pending_drills) + if (f) { - max_pending_drills += 100; - pending_drills = (PendingDrills *) realloc (pending_drills, - max_pending_drills * - sizeof (PendingDrills)); + if (n_pending_drills >= max_pending_drills) + { + max_pending_drills += 100; + pending_drills = (PendingDrills *) realloc (pending_drills, + max_pending_drills * + sizeof (PendingDrills)); + } + pending_drills[n_pending_drills].x = cx; + pending_drills[n_pending_drills].y = cy; + pending_drills[n_pending_drills].diam = radius * 2; + n_pending_drills++; + } + else + { + drillX (PCB, cx); drillY (PCB, cy); } - pending_drills[n_pending_drills].x = cx; - pending_drills[n_pending_drills].y = cy; - pending_drills[n_pending_drills].diam = radius * 2; - n_pending_drills++; return; } else if (gc->drill && !flash_drills) return; if (cx != lastX) { + double coor; lastX = cx; - fprintf (f, "X%ld", gerberX (PCB, lastX)); + coor = gerberX (PCB, lastX); + if (f) + fprintf (f, "X%.0f", coor); } if (cy != lastY) { + double coor; lastY = cy; - fprintf (f, "Y%ld", gerberY (PCB, lastY)); + coor = gerberY (PCB, lastY); + if (f) + fprintf (f, "Y%.0f", coor); } - fprintf (f, "D03*\015\012"); + if (f) + fprintf (f, "D03*\015\012"); } static void @@ -991,37 +1459,41 @@ gerber_fill_polygon (hidGC gc, int n_coords, int *x, int *y) int i; int firstTime = 1; LocationType startX = 0, startY = 0; + double coor; if (is_mask && current_mask == HID_MASK_BEFORE) return; use_gc (gc, 10 * 100); - if (!f) - return; - fprintf (f, "G36*\015\012"); + if (f) + fprintf (f, "G36*\015\012"); for (i = 0; i < n_coords; i++) { if (x[i] != lastX) { m = True; lastX = x[i]; - fprintf (f, "X%ld", gerberX (PCB, lastX)); + coor = gerberX (PCB, lastX); + if (f) + fprintf (f, "X%.0f", coor); } if (y[i] != lastY) { m = True; lastY = y[i]; - fprintf (f, "Y%ld", gerberY (PCB, lastY)); + coor = gerberY (PCB, lastY); + if (f) + fprintf (f, "Y%.0f", coor); } if (firstTime) { firstTime = 0; startX = x[i]; startY = y[i]; - if (m) + if (m && f) fprintf (f, "D02*"); } - else if (m) + else if (m && f) fprintf (f, "D01*\015\012"); m = False; } @@ -1029,14 +1501,20 @@ gerber_fill_polygon (hidGC gc, int n_coords, int *x, int *y) { m = True; lastX = startX; - fprintf (f, "X%ld", gerberX (PCB, startX)); + coor = gerberX (PCB, lastX); + if (f) + fprintf (f, "X%.0f", coor); } if (startY != lastY) { m = True; lastY = startY; - fprintf (f, "Y%ld", gerberY (PCB, lastY)); + coor = gerberY (PCB, lastY); + if (f) + fprintf (f, "Y%.0f", coor); } + if (!f) + return; if (m) fprintf (f, "D01*\015\012"); fprintf (f, "G37*\015\012");