increase the resolution for exported RS274X files from 2.3 to 3.4 format
[geda-gerbv.git] / src / draw.c
blob70547f2093cbf69eb8ea260dbb10053c9a35715a
1 /*
2 * gEDA - GNU Electronic Design Automation
3 * This file is a part of gerbv.
5 * Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
7 * $Id$
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
24 /** \file draw.c
25 \brief Cairo rendering functions and the related selection calculating functions
26 \ingroup libgerbv
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <math.h> /* ceil(), atan2() */
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
41 #include <gtk/gtk.h>
42 #include "gerbv.h"
43 #include "draw.h"
44 #include "draw-gdk.h"
45 #include <cairo.h>
47 #define dprintf if(DEBUG) printf
48 #define PIXEL_ALIGN 1
50 void
51 draw_cairo_line_to (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean adjustByHalf){
52 gdouble x1 = x, y1 = y;
53 #ifdef PIXEL_ALIGN
54 cairo_user_to_device (cairoTarget, &x1, &y1);
55 x1 = round(x1);
56 y1 = round(y1);
57 if (adjustByHalf) {
58 x1 += 0.5;
59 y1 += 0.5;
61 cairo_device_to_user (cairoTarget, &x1, &y1);
62 #endif
63 cairo_line_to (cairoTarget, x1, y1);
66 void
67 draw_cairo_move_to (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean oddWidth){
68 gdouble x1 = x, y1 = y;
69 #ifdef PIXEL_ALIGN
70 cairo_user_to_device (cairoTarget, &x1, &y1);
71 x1 = round(x1);
72 y1 = round(y1);
73 if (oddWidth) {
74 x1 += 0.5;
75 y1 += 0.5;
77 cairo_device_to_user (cairoTarget, &x1, &y1);
78 #endif
79 cairo_move_to (cairoTarget, x1, y1);
82 void
83 draw_cairo_translate_adjust (cairo_t *cairoTarget, gdouble x, gdouble y){
84 gdouble x1 = x, y1 = y;
85 #ifdef PIXEL_ALIGN
86 cairo_user_to_device (cairoTarget, &x1, &y1);
87 x1 = round(x1);
88 y1 = round(y1);
89 cairo_device_to_user (cairoTarget, &x1, &y1);
90 #endif
91 cairo_translate (cairoTarget, x1, y1);
94 gboolean
95 draw_net_in_selection_buffer (gerbv_net_t *net, gerbv_selection_info_t *selectionInfo) {
96 int i;
98 for (i=0; i<selectionInfo->selectedNodeArray->len; i++){
99 gerbv_selection_item_t sItem = g_array_index (selectionInfo->selectedNodeArray,
100 gerbv_selection_item_t, i);
101 if (sItem.net == net)
102 return TRUE;
104 return FALSE;
107 static void
108 draw_check_if_object_is_in_selected_area (cairo_t *cairoTarget, gboolean isStroke,
109 gerbv_selection_info_t *selectionInfo, gerbv_image_t *image, struct gerbv_net *net){
110 gdouble corner1X,corner1Y,corner2X,corner2Y;
112 corner1X = selectionInfo->lowerLeftX;
113 corner1Y = selectionInfo->lowerLeftY;
114 corner2X = selectionInfo->upperRightX;
115 corner2Y = selectionInfo->upperRightY;
117 /* calculate the coordinate of the user's click in the current
118 transformation matrix */
119 cairo_device_to_user (cairoTarget, &corner1X, &corner1Y);
120 cairo_device_to_user (cairoTarget, &corner2X, &corner2Y);
121 if (selectionInfo->type == GERBV_SELECTION_POINT_CLICK) {
122 /* use the cairo in_fill routine to see if the point is within the
123 drawn area */
124 if ((isStroke && cairo_in_stroke (cairoTarget, corner1X, corner1Y)) ||
125 (!isStroke && cairo_in_fill (cairoTarget, corner1X, corner1Y))) {
126 /* add the net to the selection array */
127 if (!draw_net_in_selection_buffer(net, selectionInfo)) {
128 gerbv_selection_item_t sItem = {image, net};
129 g_array_append_val (selectionInfo->selectedNodeArray, sItem);
133 else if (selectionInfo->type == GERBV_SELECTION_DRAG_BOX) {
134 gdouble x1,x2,y1,y2;
135 gdouble minX,minY,maxX,maxY;
137 /* we can't assume the "lowerleft" corner is actually in the lower left,
138 since the cairo transformation matrix may be mirrored,etc */
139 minX = MIN(corner1X,corner2X);
140 maxX = MAX(corner1X,corner2X);
141 minY = MIN(corner1Y,corner2Y);
142 maxY = MAX(corner1Y,corner2Y);
143 if (isStroke)
144 cairo_stroke_extents (cairoTarget, &x1, &y1, &x2, &y2);
145 else
146 cairo_fill_extents (cairoTarget, &x1, &y1, &x2, &y2);
148 if ((minX < x1) && (minY < y1) && (maxX > x2) && (maxY > y2)) {
149 /* add the net to the selection array */
150 if (!draw_net_in_selection_buffer(net, selectionInfo)) {
151 gerbv_selection_item_t sItem = {image, net};
152 g_array_append_val (selectionInfo->selectedNodeArray, sItem);
156 /* clear the path, since we didn't actually draw it and cairo
157 doesn't reset it after the previous calls */
158 cairo_new_path (cairoTarget);
161 static void
162 draw_fill (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
163 gerbv_image_t *image, struct gerbv_net *net){
164 if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
165 cairo_fill (cairoTarget);
166 else
167 draw_check_if_object_is_in_selected_area (cairoTarget, FALSE,
168 selectionInfo, image, net);
171 static void
172 draw_stroke (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
173 gerbv_image_t *image, struct gerbv_net *net){
174 if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
175 cairo_stroke (cairoTarget);
176 else
177 draw_check_if_object_is_in_selected_area (cairoTarget, TRUE,
178 selectionInfo, image, net);
182 * Draws a circle _centered_ at x,y with diameter dia
184 static void
185 gerbv_draw_circle(cairo_t *cairoTarget, gdouble diameter)
187 cairo_arc (cairoTarget, 0.0, 0.0, diameter/2.0, 0, 2.0*M_PI);
188 return;
189 } /* gerbv_draw_circle */
193 * Draws a rectangle _centered_ at x,y with sides x_side, y_side
195 static void
196 gerbv_draw_rectangle(cairo_t *cairoTarget, gdouble width1, gdouble height1)
198 gdouble width = width1, height = height1;
199 cairo_user_to_device_distance (cairoTarget, &width, &height);
200 width = round(width);
201 height = round(height);
202 width -= ((int)width % 2);
203 height -= ((int)height % 2);
204 cairo_device_to_user_distance (cairoTarget, &width, &height);
206 cairo_rectangle (cairoTarget, - width / 2.0, - height / 2.0, width, height);
207 return;
208 } /* gerbv_draw_rectangle */
212 * Draws an oblong _centered_ at x,y with x axis x_axis and y axis y_axis
214 static void
215 gerbv_draw_oblong(cairo_t *cairoTarget, gdouble width, gdouble height)
217 /* --- This stuff produces a line + rounded ends --- */
218 gdouble circleDiameter, strokeDistance;
220 cairo_new_path (cairoTarget);
221 if (width < height) {
222 circleDiameter = width;
223 strokeDistance = (height - width)/2.0;
224 cairo_arc (cairoTarget, 0.0, strokeDistance, circleDiameter/2.0, 0, -M_PI);
225 cairo_line_to (cairoTarget, -circleDiameter/2.0, -strokeDistance);
226 cairo_arc (cairoTarget, 0.0, -strokeDistance, circleDiameter/2.0, -M_PI, 0);
227 cairo_line_to (cairoTarget, circleDiameter/2.0, strokeDistance);
229 else {
230 circleDiameter = height;
231 strokeDistance = (width - height)/2.0;
232 cairo_arc (cairoTarget, -strokeDistance, 0.0, circleDiameter/2.0, M_PI/2.0, -M_PI/2.0);
233 cairo_line_to (cairoTarget, strokeDistance, -circleDiameter/2.0);
234 cairo_arc (cairoTarget, strokeDistance, 0.0, circleDiameter/2.0, -M_PI/2.0, M_PI/2.0);
235 cairo_line_to (cairoTarget, -strokeDistance, circleDiameter/2.0);
237 /* --- This stuff produces an oval pad --- */
238 /* cairo doesn't have a function to draw ovals, so we must
239 * draw an arc and stretch it by scaling different x and y values
240 cairo_save (cairoTarget);
241 cairo_scale (cairoTarget, width, height);
242 gerbv_draw_circle (cairoTarget, 1);
243 cairo_restore (cairoTarget);
245 return;
246 } /* gerbv_draw_oblong */
249 static void
250 gerbv_draw_polygon(cairo_t *cairoTarget, gdouble outsideDiameter,
251 gdouble numberOfSides, gdouble degreesOfRotation)
253 int i, numberOfSidesInteger = (int) numberOfSides;
255 cairo_rotate(cairoTarget, degreesOfRotation * M_PI/180);
256 cairo_move_to(cairoTarget, outsideDiameter / 2.0, 0);
257 /* skip first point, since we've moved there already */
258 /* include last point, since we may be drawing an aperture hole next
259 and cairo may not correctly close the path itself */
260 for (i = 1; i <= (int)numberOfSidesInteger; i++){
261 gdouble angle = (double) i / numberOfSidesInteger * M_PI * 2.0;
262 cairo_line_to (cairoTarget, cos(angle) * outsideDiameter / 2.0,
263 sin(angle) * outsideDiameter / 2.0);
265 return;
266 } /* gerbv_draw_polygon */
269 static void
270 gerbv_draw_aperature_hole(cairo_t *cairoTarget, gdouble dimensionX, gdouble dimensionY)
272 if (dimensionX) {
273 if (dimensionY) {
274 gerbv_draw_rectangle (cairoTarget, dimensionX, dimensionY);
275 } else {
276 gerbv_draw_circle (cairoTarget, dimensionX);
279 return;
280 } /* gerbv_draw_aperature_hole */
282 gboolean
283 draw_update_macro_exposure (cairo_t *cairoTarget, cairo_operator_t clearOperator,
284 cairo_operator_t darkOperator, gdouble exposureSetting){
286 if (exposureSetting == 0.0) {
287 cairo_set_operator (cairoTarget, clearOperator);
289 else if (exposureSetting == 1.0) {
290 cairo_set_operator (cairoTarget, darkOperator);
292 else if (exposureSetting == 2.0) {
293 /* reverse current exposure setting */
294 cairo_operator_t currentOperator = cairo_get_operator (cairoTarget);
295 if (currentOperator == clearOperator) {
296 cairo_set_operator (cairoTarget, darkOperator);
298 else {
299 cairo_set_operator (cairoTarget, clearOperator);
302 return TRUE;
307 gerbv_draw_amacro(cairo_t *cairoTarget, cairo_operator_t clearOperator,
308 cairo_operator_t darkOperator, gerbv_simplified_amacro_t *s,
309 gint usesClearPrimative, gdouble pixelWidth, gchar drawMode,
310 gerbv_selection_info_t *selectionInfo,
311 gerbv_image_t *image, struct gerbv_net *net)
313 int handled = 1;
314 gerbv_simplified_amacro_t *ls = s;
316 dprintf("Drawing simplified aperture macros:\n");
317 if (usesClearPrimative)
318 cairo_push_group (cairoTarget);
319 while (ls != NULL) {
321 * This handles the exposure thing in the aperture macro
322 * The exposure is always the first element on stack independent
323 * of aperture macro.
325 cairo_save (cairoTarget);
326 cairo_new_path(cairoTarget);
327 cairo_operator_t oldOperator = cairo_get_operator (cairoTarget);
329 if (ls->type == GERBV_APTYPE_MACRO_CIRCLE) {
331 if (draw_update_macro_exposure (cairoTarget, clearOperator,
332 darkOperator, ls->parameter[CIRCLE_EXPOSURE])){
333 cairo_translate (cairoTarget, ls->parameter[CIRCLE_CENTER_X],
334 ls->parameter[CIRCLE_CENTER_Y]);
336 gerbv_draw_circle (cairoTarget, ls->parameter[CIRCLE_DIAMETER]);
337 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
339 } else if (ls->type == GERBV_APTYPE_MACRO_OUTLINE) {
340 int pointCounter,numberOfPoints;
341 /* Number of points parameter seems to not include the start point,
342 * so we add one to include the start point.
344 numberOfPoints = (int) ls->parameter[OUTLINE_NUMBER_OF_POINTS] + 1;
346 if (draw_update_macro_exposure (cairoTarget, clearOperator,
347 darkOperator, ls->parameter[OUTLINE_EXPOSURE])){
348 cairo_rotate (cairoTarget, ls->parameter[(numberOfPoints - 1) * 2 + OUTLINE_ROTATION] * M_PI/180.0);
349 cairo_move_to (cairoTarget, ls->parameter[OUTLINE_FIRST_X], ls->parameter[OUTLINE_FIRST_Y]);
351 for (pointCounter=0; pointCounter < numberOfPoints; pointCounter++) {
352 cairo_line_to (cairoTarget, ls->parameter[pointCounter * 2 + OUTLINE_FIRST_X],
353 ls->parameter[pointCounter * 2 + OUTLINE_FIRST_Y]);
355 /* although the gerber specs allow for an open outline,
356 I interpret it to mean the outline should be closed by the
357 rendering softare automatically, since there is no dimension
358 for line thickness.
360 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
362 } else if (ls->type == GERBV_APTYPE_MACRO_POLYGON) {
363 if (draw_update_macro_exposure (cairoTarget, clearOperator,
364 darkOperator, ls->parameter[POLYGON_EXPOSURE])){
365 cairo_translate (cairoTarget, ls->parameter[POLYGON_CENTER_X],
366 ls->parameter[POLYGON_CENTER_Y]);
367 gerbv_draw_polygon(cairoTarget, ls->parameter[POLYGON_DIAMETER],
368 ls->parameter[POLYGON_NUMBER_OF_POINTS], ls->parameter[POLYGON_ROTATION]);
369 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
371 } else if (ls->type == GERBV_APTYPE_MACRO_MOIRE) {
372 gdouble diameter, gap;
373 int circleIndex;
375 cairo_translate (cairoTarget, ls->parameter[MOIRE_CENTER_X],
376 ls->parameter[MOIRE_CENTER_Y]);
377 cairo_rotate (cairoTarget, ls->parameter[MOIRE_ROTATION] * M_PI/180);
378 diameter = ls->parameter[MOIRE_OUTSIDE_DIAMETER] - ls->parameter[MOIRE_CIRCLE_THICKNESS];
379 gap = ls->parameter[MOIRE_GAP_WIDTH] + ls->parameter[MOIRE_CIRCLE_THICKNESS];
380 cairo_set_line_width (cairoTarget, ls->parameter[MOIRE_CIRCLE_THICKNESS]);
382 for (circleIndex = 0; circleIndex < (int)ls->parameter[MOIRE_NUMBER_OF_CIRCLES]; circleIndex++) {
383 gdouble currentDiameter = (diameter - gap * (float) circleIndex);
384 gerbv_draw_circle (cairoTarget, currentDiameter);
385 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
388 gdouble crosshairRadius = (ls->parameter[MOIRE_CROSSHAIR_LENGTH] / 2.0);
390 cairo_set_line_width (cairoTarget, ls->parameter[MOIRE_CROSSHAIR_THICKNESS]);
391 cairo_move_to (cairoTarget, -crosshairRadius, 0);
392 cairo_line_to (cairoTarget, crosshairRadius, 0);
393 cairo_move_to (cairoTarget, 0, -crosshairRadius);
394 cairo_line_to (cairoTarget, 0, crosshairRadius);
395 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
396 } else if (ls->type == GERBV_APTYPE_MACRO_THERMAL) {
397 gint i;
398 gdouble startAngle1, startAngle2, endAngle1, endAngle2;
400 cairo_translate (cairoTarget, ls->parameter[THERMAL_CENTER_X],
401 ls->parameter[THERMAL_CENTER_Y]);
402 cairo_rotate (cairoTarget, ls->parameter[THERMAL_ROTATION] * M_PI/180.0);
403 startAngle1 = atan (ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/ls->parameter[THERMAL_INSIDE_DIAMETER]);
404 endAngle1 = M_PI/2 - startAngle1;
405 endAngle2 = atan (ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/ls->parameter[THERMAL_OUTSIDE_DIAMETER]);
406 startAngle2 = M_PI/2 - endAngle2;
407 for (i = 0; i < 4; i++) {
408 cairo_arc (cairoTarget, 0, 0, ls->parameter[THERMAL_INSIDE_DIAMETER]/2.0, startAngle1, endAngle1);
409 cairo_rel_line_to (cairoTarget, 0, ls->parameter[THERMAL_CROSSHAIR_THICKNESS]);
410 cairo_arc_negative (cairoTarget, 0, 0, ls->parameter[THERMAL_OUTSIDE_DIAMETER]/2.0,
411 startAngle2, endAngle2);
412 cairo_rel_line_to (cairoTarget, -ls->parameter[THERMAL_CROSSHAIR_THICKNESS],0);
413 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
414 cairo_rotate (cairoTarget, 90 * M_PI/180);
416 } else if (ls->type == GERBV_APTYPE_MACRO_LINE20) {
417 if (draw_update_macro_exposure (cairoTarget, clearOperator,
418 darkOperator, ls->parameter[LINE20_EXPOSURE])){
419 gdouble cParameter = ls->parameter[LINE20_LINE_WIDTH];
420 if (cParameter < pixelWidth)
421 cParameter = pixelWidth;
423 cairo_set_line_width (cairoTarget, cParameter);
424 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_BUTT);
425 cairo_rotate (cairoTarget, ls->parameter[LINE20_ROTATION] * M_PI/180.0);
426 cairo_move_to (cairoTarget, ls->parameter[LINE20_START_X], ls->parameter[LINE20_START_Y]);
427 cairo_line_to (cairoTarget, ls->parameter[LINE20_END_X], ls->parameter[LINE20_END_Y]);
428 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
430 } else if (ls->type == GERBV_APTYPE_MACRO_LINE21) {
431 gdouble halfWidth, halfHeight;
433 if (draw_update_macro_exposure (cairoTarget, clearOperator,
434 darkOperator, ls->parameter[LINE22_EXPOSURE])){
435 halfWidth = ls->parameter[LINE21_WIDTH] / 2.0;
436 halfHeight = ls->parameter[LINE21_HEIGHT] / 2.0;
437 if (halfWidth < pixelWidth)
438 halfWidth = pixelWidth;
439 if (halfHeight < pixelWidth)
440 halfHeight = pixelWidth;
441 cairo_translate (cairoTarget, ls->parameter[LINE21_CENTER_X], ls->parameter[LINE21_CENTER_Y]);
442 cairo_rotate (cairoTarget, ls->parameter[LINE21_ROTATION] * M_PI/180.0);
443 cairo_rectangle (cairoTarget, -halfWidth, -halfHeight,
444 ls->parameter[LINE21_WIDTH], ls->parameter[LINE21_HEIGHT]);
445 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
447 } else if (ls->type == GERBV_APTYPE_MACRO_LINE22) {
448 gdouble halfWidth, halfHeight;
450 if (draw_update_macro_exposure (cairoTarget, clearOperator,
451 darkOperator, ls->parameter[LINE22_EXPOSURE])){
452 halfWidth = ls->parameter[LINE22_WIDTH] / 2.0;
453 halfHeight = ls->parameter[LINE22_HEIGHT] / 2.0;
454 if (halfWidth < pixelWidth)
455 halfWidth = pixelWidth;
456 if (halfHeight < pixelWidth)
457 halfHeight = pixelWidth;
458 cairo_translate (cairoTarget, ls->parameter[LINE22_LOWER_LEFT_X],
459 ls->parameter[LINE22_LOWER_LEFT_Y]);
460 cairo_rotate (cairoTarget, ls->parameter[LINE22_ROTATION] * M_PI/180.0);
461 cairo_rectangle (cairoTarget, 0, 0,
462 ls->parameter[LINE22_WIDTH], ls->parameter[LINE22_HEIGHT]);
463 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
465 } else {
466 handled = 0;
468 cairo_set_operator (cairoTarget, oldOperator);
469 cairo_restore (cairoTarget);
470 ls = ls->next;
472 if (usesClearPrimative) {
473 cairo_pop_group_to_source (cairoTarget);
474 cairo_paint (cairoTarget);
476 return handled;
477 } /* gerbv_draw_amacro */
480 void
481 draw_apply_netstate_transformation (cairo_t *cairoTarget, gerbv_netstate_t *state)
483 /* apply scale factor */
484 cairo_scale (cairoTarget, state->scaleA, state->scaleB);
485 /* apply offset */
486 cairo_translate (cairoTarget, state->offsetA, state->offsetB);
487 /* apply mirror */
488 switch (state->mirrorState) {
489 case GERBV_MIRROR_STATE_FLIPA:
490 cairo_scale (cairoTarget, -1, 1);
491 break;
492 case GERBV_MIRROR_STATE_FLIPB:
493 cairo_scale (cairoTarget, 1, -1);
494 break;
495 case GERBV_MIRROR_STATE_FLIPAB:
496 cairo_scale (cairoTarget, -1, -1);
497 break;
498 default:
499 break;
501 /* finally, apply axis select */
502 if (state->axisSelect == GERBV_AXIS_SELECT_SWAPAB) {
503 /* we do this by rotating 270 (counterclockwise, then mirroring
504 the Y axis */
505 cairo_rotate (cairoTarget, 3 * M_PI / 2);
506 cairo_scale (cairoTarget, 1, -1);
510 void
511 draw_render_polygon_object (gerbv_net_t *oldNet, cairo_t *cairoTarget, gdouble sr_x, gdouble sr_y,
512 gerbv_image_t *image, gchar drawMode, gerbv_selection_info_t *selectionInfo ){
513 gerbv_net_t *currentNet, *polygonStartNet;
514 int haveDrawnFirstFillPoint = 0;
515 gdouble x1,y1,x2,y2,cp_x=0,cp_y=0;
517 haveDrawnFirstFillPoint = FALSE;
518 /* save the first net in the polygon as the "ID" net pointer
519 in case we are saving this net to the selection array */
520 polygonStartNet = oldNet;
521 cairo_new_path(cairoTarget);
523 for (currentNet = oldNet->next; currentNet!=NULL; currentNet = currentNet->next){
524 x1 = currentNet->start_x + sr_x;
525 y1 = currentNet->start_y + sr_y;
526 x2 = currentNet->stop_x + sr_x;
527 y2 = currentNet->stop_y + sr_y;
529 /* translate circular x,y data as well */
530 if (currentNet->cirseg) {
531 cp_x = currentNet->cirseg->cp_x + sr_x;
532 cp_y = currentNet->cirseg->cp_y + sr_y;
534 if (!haveDrawnFirstFillPoint) {
535 draw_cairo_move_to (cairoTarget, x2, y2, FALSE);
536 haveDrawnFirstFillPoint=TRUE;
537 continue;
539 switch (currentNet->interpolation) {
540 case GERBV_INTERPOLATION_x10 :
541 case GERBV_INTERPOLATION_LINEARx01 :
542 case GERBV_INTERPOLATION_LINEARx001 :
543 case GERBV_INTERPOLATION_LINEARx1 :
544 draw_cairo_line_to (cairoTarget, x2, y2, FALSE);
545 break;
546 case GERBV_INTERPOLATION_CW_CIRCULAR :
547 case GERBV_INTERPOLATION_CCW_CIRCULAR :
548 if (currentNet->cirseg->angle2 > currentNet->cirseg->angle1) {
549 cairo_arc (cairoTarget, cp_x, cp_y, currentNet->cirseg->width/2.0,
550 currentNet->cirseg->angle1 * M_PI/180,currentNet->cirseg->angle2 * M_PI/180);
552 else {
553 cairo_arc_negative (cairoTarget, cp_x, cp_y, currentNet->cirseg->width/2.0,
554 currentNet->cirseg->angle1 * M_PI/180,currentNet->cirseg->angle2 * M_PI/180);
556 break;
557 case GERBV_INTERPOLATION_PAREA_END :
558 cairo_close_path(cairoTarget);
559 /* turn off anti-aliasing for polygons, since it shows seams
560 with adjacent polygons (usually on PCB ground planes) */
561 cairo_antialias_t oldAlias = cairo_get_antialias (cairoTarget);
562 cairo_set_antialias (cairoTarget, CAIRO_ANTIALIAS_NONE);
563 draw_fill (cairoTarget, drawMode, selectionInfo, image, polygonStartNet);
564 cairo_set_antialias (cairoTarget, oldAlias);
565 return;
566 default :
567 break;
575 draw_image_to_cairo_target (cairo_t *cairoTarget, gerbv_image_t *image,
576 gdouble pixelWidth,
577 gchar drawMode, gerbv_selection_info_t *selectionInfo,
578 gerbv_render_info_t *renderInfo, gboolean allowOptimization,
579 gerbv_user_transformation_t transform, gboolean limitPixelSize){
580 struct gerbv_net *net, *polygonStartNet=NULL;
581 double x1, y1, x2, y2, cp_x=0, cp_y=0;
582 gdouble p1, p2, p3, p4, p5, dx, dy, lineWidth;
583 gerbv_netstate_t *oldState;
584 gerbv_layer_t *oldLayer;
585 int repeat_X=1, repeat_Y=1;
586 double repeat_dist_X = 0, repeat_dist_Y = 0;
587 int repeat_i, repeat_j;
588 cairo_operator_t drawOperatorClear, drawOperatorDark;
589 gboolean invertPolarity = FALSE, oddWidth = FALSE;
590 gdouble minX=0, minY=0, maxX=0, maxY=0;
591 gdouble criticalRadius;
592 gdouble scaleX = transform.scaleX;
593 gdouble scaleY = transform.scaleY;
595 if (transform.mirrorAroundX)
596 scaleY *= -1;
597 if (transform.mirrorAroundY)
598 scaleX *= -1;
599 cairo_translate (cairoTarget, transform.translateX, transform.translateY);
600 cairo_scale (cairoTarget, scaleX, scaleY);
601 cairo_rotate (cairoTarget, transform.rotation);
603 gboolean useOptimizations = allowOptimization;
604 // if the user is using any transformations for this layer, then don't bother using rendering
605 // optimizations
606 if ((fabs(transform.translateX) > 0.00001) ||
607 (fabs(transform.translateY) > 0.00001) ||
608 (fabs(transform.scaleX - 1) > 0.00001) ||
609 (fabs(transform.scaleY - 1) > 0.00001) ||
610 (fabs(transform.rotation) > 0.00001) ||
611 transform.mirrorAroundX || transform.mirrorAroundY)
612 useOptimizations = FALSE;
614 if (useOptimizations) {
615 minX = renderInfo->lowerLeftX;
616 minY = renderInfo->lowerLeftY;
617 maxX = renderInfo->lowerLeftX + (renderInfo->displayWidth /
618 renderInfo->scaleFactorX);
619 maxY = renderInfo->lowerLeftY + (renderInfo->displayHeight /
620 renderInfo->scaleFactorY);
623 /* do initial justify */
624 cairo_translate (cairoTarget, image->info->imageJustifyOffsetActualA,
625 image->info->imageJustifyOffsetActualB);
627 /* set the fill rule so aperture holes are cleared correctly */
628 cairo_set_fill_rule (cairoTarget, CAIRO_FILL_RULE_EVEN_ODD);
629 /* offset image */
630 cairo_translate (cairoTarget, image->info->offsetA, image->info->offsetB);
631 /* do image rotation */
632 cairo_rotate (cairoTarget, image->info->imageRotation);
633 /* load in polarity operators depending on the image polarity */
634 invertPolarity = transform.inverted;
635 if (image->info->polarity == GERBV_POLARITY_NEGATIVE)
636 invertPolarity = !invertPolarity;
637 if (drawMode == DRAW_SELECTIONS)
638 invertPolarity = FALSE;
640 if (invertPolarity) {
641 drawOperatorClear = CAIRO_OPERATOR_OVER;
642 drawOperatorDark = CAIRO_OPERATOR_CLEAR;
643 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
644 cairo_paint (cairoTarget);
645 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
647 else {
648 drawOperatorClear = CAIRO_OPERATOR_CLEAR;
649 drawOperatorDark = CAIRO_OPERATOR_OVER;
651 /* next, push two cairo states to simulate the first layer and netstate
652 translations (these will be popped when another layer or netstate is
653 started */
655 cairo_save (cairoTarget);
656 cairo_save (cairoTarget);
657 /* store the current layer and netstate so we know when they change */
658 oldLayer = image->layers;
659 oldState = image->states;
661 for (net = image->netlist->next ; net != NULL; net = gerbv_image_return_next_renderable_object(net)) {
663 /* check if this is a new layer */
664 if (net->layer != oldLayer){
665 /* it's a new layer, so recalculate the new transformation matrix
666 for it */
667 cairo_restore (cairoTarget);
668 cairo_restore (cairoTarget);
669 cairo_save (cairoTarget);
670 /* do any rotations */
671 cairo_rotate (cairoTarget, net->layer->rotation);
672 /* handle the layer polarity */
673 if ((net->layer->polarity == GERBV_POLARITY_CLEAR)^invertPolarity) {
674 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
675 drawOperatorClear = CAIRO_OPERATOR_OVER;
676 drawOperatorDark = CAIRO_OPERATOR_CLEAR;
678 else {
679 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
680 drawOperatorClear = CAIRO_OPERATOR_CLEAR;
681 drawOperatorDark = CAIRO_OPERATOR_OVER;
683 /* check for changes to step and repeat */
684 repeat_X = net->layer->stepAndRepeat.X;
685 repeat_Y = net->layer->stepAndRepeat.Y;
686 repeat_dist_X = net->layer->stepAndRepeat.dist_X;
687 repeat_dist_Y = net->layer->stepAndRepeat.dist_Y;
688 /* draw any knockout areas */
689 if (net->layer->knockout.firstInstance == TRUE) {
690 cairo_operator_t oldOperator = cairo_get_operator (cairoTarget);
691 if (net->layer->knockout.polarity == GERBV_POLARITY_CLEAR) {
692 cairo_set_operator (cairoTarget, drawOperatorClear);
694 else {
695 cairo_set_operator (cairoTarget, drawOperatorDark);
697 cairo_new_path (cairoTarget);
698 cairo_rectangle (cairoTarget, net->layer->knockout.lowerLeftX - net->layer->knockout.border,
699 net->layer->knockout.lowerLeftY - net->layer->knockout.border,
700 net->layer->knockout.width + (net->layer->knockout.border*2),
701 net->layer->knockout.height + (net->layer->knockout.border*2));
702 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
703 cairo_set_operator (cairoTarget, oldOperator);
705 /* finally, reapply old netstate transformation */
706 cairo_save (cairoTarget);
707 draw_apply_netstate_transformation (cairoTarget, net->state);
708 oldLayer = net->layer;
710 /* check if this is a new netstate */
711 if (net->state != oldState){
712 /* pop the transformation matrix back to the "pre-state" state and
713 resave it */
714 cairo_restore (cairoTarget);
715 cairo_save (cairoTarget);
716 /* it's a new state, so recalculate the new transformation matrix
717 for it */
718 draw_apply_netstate_transformation (cairoTarget, net->state);
719 oldState = net->state;
721 /* if we are only drawing from the selection buffer, search if this net is
722 in the buffer */
723 if (drawMode == DRAW_SELECTIONS) {
724 /* this flag makes sure we don't draw any unintentional polygons...
725 if we've successfully entered a polygon (the first net matches, and
726 we don't want to check the nets inside the polygon) then
727 polygonStartNet will be set */
728 if (!polygonStartNet) {
729 if (!draw_net_in_selection_buffer(net, selectionInfo))
730 continue;
733 for(repeat_i = 0; repeat_i < repeat_X; repeat_i++) {
734 for(repeat_j = 0; repeat_j < repeat_Y; repeat_j++) {
735 double sr_x = repeat_i * repeat_dist_X;
736 double sr_y = repeat_j * repeat_dist_Y;
739 if ((useOptimizations) &&
740 ((net->boundingBox.right+sr_x < minX)
741 || (net->boundingBox.left+sr_x > maxX)
742 || (net->boundingBox.top+sr_y < minY)
743 || (net->boundingBox.bottom+sr_y > maxY))) {
744 continue;
747 x1 = net->start_x + sr_x;
748 y1 = net->start_y + sr_y;
749 x2 = net->stop_x + sr_x;
750 y2 = net->stop_y + sr_y;
752 /* translate circular x,y data as well */
753 if (net->cirseg) {
754 cp_x = net->cirseg->cp_x + sr_x;
755 cp_y = net->cirseg->cp_y + sr_y;
758 /* render any labels attached to this net */
759 /* NOTE: this is currently only used on PNP files, so we may
760 make some assumptions here... */
761 if (net->label) {
762 cairo_set_font_size (cairoTarget, 0.05);
763 cairo_save (cairoTarget);
765 cairo_move_to (cairoTarget, x1, y1);
766 cairo_scale (cairoTarget, 1, -1);
767 cairo_show_text (cairoTarget, net->label->str);
768 cairo_restore (cairoTarget);
771 * Polygon Area Fill routines
773 switch (net->interpolation) {
774 case GERBV_INTERPOLATION_PAREA_START :
775 draw_render_polygon_object (net, cairoTarget, sr_x, sr_y, image,
776 drawMode, selectionInfo);
777 continue;
778 case GERBV_INTERPOLATION_DELETED:
779 continue;
780 default :
781 break;
785 * If aperture state is off we allow use of undefined apertures.
786 * This happens when gerber files starts, but hasn't decided on
787 * which aperture to use.
789 if (image->aperture[net->aperture] == NULL) {
790 /* Commenting this out since it gets emitted every time you click on the screen
791 if (net->aperture_state != GERBV_APERTURE_STATE_OFF)
792 GERB_MESSAGE("Aperture D%d is not defined\n", net->aperture);
794 continue;
796 switch (net->aperture_state) {
797 case GERBV_APERTURE_STATE_ON :
798 /* if the aperture width is truly 0, then render as a 1 pixel width
799 line. 0 diameter apertures are used by some programs to draw labels,
800 etc, and they are rendered by other programs as 1 pixel wide */
801 /* NOTE: also, make sure all lines are at least 1 pixel wide, so they
802 always show up at low zoom levels */
804 if ((image->aperture[net->aperture]->parameter[0] < pixelWidth)&&
805 (limitPixelSize))
806 criticalRadius = pixelWidth/2.0;
808 else if (image->aperture[net->aperture]->parameter[0] == 0)
809 criticalRadius = pixelWidth/2.0;
810 else if (random() < (RAND_MAX / 10))
811 criticalRadius = pixelWidth/2.0;
812 else
813 break;
815 else
816 criticalRadius = image->aperture[net->aperture]->parameter[0]/2.0;
817 lineWidth = criticalRadius*2.0;
818 // convert to a pixel integer
819 cairo_user_to_device_distance (cairoTarget, &lineWidth, &x1);
820 lineWidth = round(lineWidth);
821 if ((int)lineWidth % 2) {
822 oddWidth = TRUE;
824 else {
825 oddWidth = FALSE;
827 cairo_device_to_user_distance (cairoTarget, &lineWidth, &x1);
828 cairo_set_line_width (cairoTarget, lineWidth);
829 switch (net->interpolation) {
830 case GERBV_INTERPOLATION_x10 :
831 case GERBV_INTERPOLATION_LINEARx01 :
832 case GERBV_INTERPOLATION_LINEARx001 :
833 case GERBV_INTERPOLATION_LINEARx1 :
834 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
835 // weed out any lines that are obviously not going to render on the
836 // visible screen
838 switch (image->aperture[net->aperture]->type) {
839 case GERBV_APTYPE_CIRCLE :
840 draw_cairo_move_to (cairoTarget, x1, y1, oddWidth);
841 draw_cairo_line_to (cairoTarget, x2, y2, oddWidth);
842 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
843 break;
844 case GERBV_APTYPE_RECTANGLE :
845 dx = (image->aperture[net->aperture]->parameter[0]/ 2);
846 dy = (image->aperture[net->aperture]->parameter[1]/ 2);
847 if(x1 > x2)
848 dx = -dx;
849 if(y1 > y2)
850 dy = -dy;
851 cairo_new_path(cairoTarget);
852 draw_cairo_move_to (cairoTarget, x1 - dx, y1 - dy, FALSE);
853 draw_cairo_line_to (cairoTarget, x1 - dx, y1 + dy, FALSE);
854 draw_cairo_line_to (cairoTarget, x2 - dx, y2 + dy, FALSE);
855 draw_cairo_line_to (cairoTarget, x2 + dx, y2 + dy, FALSE);
856 draw_cairo_line_to (cairoTarget, x2 + dx, y2 - dy, FALSE);
857 draw_cairo_line_to (cairoTarget, x1 + dx, y1 - dy, FALSE);
858 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
859 break;
860 /* for now, just render ovals or polygons like a circle */
861 case GERBV_APTYPE_OVAL :
862 case GERBV_APTYPE_POLYGON :
863 draw_cairo_move_to (cairoTarget, x1,y1, oddWidth);
864 draw_cairo_line_to (cairoTarget, x2,y2, oddWidth);
865 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
866 break;
867 /* macros can only be flashed, so ignore any that might be here */
868 default :
869 break;
871 break;
872 case GERBV_INTERPOLATION_CW_CIRCULAR :
873 case GERBV_INTERPOLATION_CCW_CIRCULAR :
874 /* cairo doesn't have a function to draw oval arcs, so we must
875 * draw an arc and stretch it by scaling different x and y values
877 cairo_new_path(cairoTarget);
878 if (image->aperture[net->aperture]->type == GERBV_APTYPE_RECTANGLE) {
879 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_SQUARE);
881 else {
882 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
884 cairo_save (cairoTarget);
885 cairo_translate(cairoTarget, cp_x, cp_y);
886 cairo_scale (cairoTarget, net->cirseg->width, net->cirseg->height);
887 if (net->cirseg->angle2 > net->cirseg->angle1) {
888 cairo_arc (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
889 net->cirseg->angle2 * M_PI/180);
891 else {
892 cairo_arc_negative (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
893 net->cirseg->angle2 * M_PI/180);
895 cairo_restore (cairoTarget);
896 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
897 break;
898 default :
899 break;
901 break;
902 case GERBV_APERTURE_STATE_OFF :
903 break;
904 case GERBV_APERTURE_STATE_FLASH :
905 p1 = image->aperture[net->aperture]->parameter[0];
906 p2 = image->aperture[net->aperture]->parameter[1];
907 p3 = image->aperture[net->aperture]->parameter[2];
908 p4 = image->aperture[net->aperture]->parameter[3];
909 p5 = image->aperture[net->aperture]->parameter[4];
911 cairo_save (cairoTarget);
912 draw_cairo_translate_adjust(cairoTarget, x2, y2);
914 switch (image->aperture[net->aperture]->type) {
915 case GERBV_APTYPE_CIRCLE :
916 gerbv_draw_circle(cairoTarget, p1);
917 gerbv_draw_aperature_hole (cairoTarget, p2, p3);
918 break;
919 case GERBV_APTYPE_RECTANGLE :
920 gerbv_draw_rectangle(cairoTarget, p1, p2);
921 gerbv_draw_aperature_hole (cairoTarget, p3, p4);
922 break;
923 case GERBV_APTYPE_OVAL :
924 gerbv_draw_oblong(cairoTarget, p1, p2);
925 gerbv_draw_aperature_hole (cairoTarget, p3, p4);
926 break;
927 case GERBV_APTYPE_POLYGON :
928 gerbv_draw_polygon(cairoTarget, p1, p2, p3);
929 gerbv_draw_aperature_hole (cairoTarget, p4, p5);
930 break;
931 case GERBV_APTYPE_MACRO :
932 gerbv_draw_amacro(cairoTarget, drawOperatorClear, drawOperatorDark,
933 image->aperture[net->aperture]->simplified,
934 (int) image->aperture[net->aperture]->parameter[0], pixelWidth,
935 drawMode, selectionInfo, image, net);
936 break;
937 default :
938 GERB_MESSAGE("Unknown aperture type\n");
939 return 0;
941 /* and finally fill the path */
942 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
943 cairo_restore (cairoTarget);
944 break;
945 default:
946 GERB_MESSAGE("Unknown aperture state\n");
947 return 0;
953 /* restore the initial two state saves (one for layer, one for netstate)*/
954 cairo_restore (cairoTarget);
955 cairo_restore (cairoTarget);
957 return 1;