Implemented popup window to select which files in the zip archive to
[geda-gerbv/spe.git] / src / draw.c
blob334a4064344fd48b436dcb165fc368471594c4ca
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
49 void
50 draw_cairo_line_to (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean adjustByHalf, gboolean pixelOutput){
51 gdouble x1 = x, y1 = y;
52 if (pixelOutput) {
53 cairo_user_to_device (cairoTarget, &x1, &y1);
54 x1 = round(x1);
55 y1 = round(y1);
56 if (adjustByHalf) {
57 x1 += 0.5;
58 y1 += 0.5;
60 cairo_device_to_user (cairoTarget, &x1, &y1);
62 cairo_line_to (cairoTarget, x1, y1);
65 void
66 draw_cairo_move_to (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean oddWidth, gboolean pixelOutput){
67 gdouble x1 = x, y1 = y;
68 if (pixelOutput) {
69 cairo_user_to_device (cairoTarget, &x1, &y1);
70 x1 = round(x1);
71 y1 = round(y1);
72 if (oddWidth) {
73 x1 += 0.5;
74 y1 += 0.5;
76 cairo_device_to_user (cairoTarget, &x1, &y1);
78 cairo_move_to (cairoTarget, x1, y1);
81 void
82 draw_cairo_translate_adjust (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean pixelOutput){
83 gdouble x1 = x, y1 = y;
84 if (pixelOutput) {
85 cairo_user_to_device (cairoTarget, &x1, &y1);
86 x1 = round(x1);
87 y1 = round(y1);
88 cairo_device_to_user (cairoTarget, &x1, &y1);
90 cairo_translate (cairoTarget, x1, y1);
93 gboolean
94 draw_net_in_selection_buffer (gerbv_net_t *net, gerbv_selection_info_t *selectionInfo) {
95 int i;
97 for (i=0; i<selectionInfo->selectedNodeArray->len; i++){
98 gerbv_selection_item_t sItem = g_array_index (selectionInfo->selectedNodeArray,
99 gerbv_selection_item_t, i);
100 if (sItem.net == net)
101 return TRUE;
103 return FALSE;
106 static void
107 draw_check_if_object_is_in_selected_area (cairo_t *cairoTarget, gboolean isStroke,
108 gerbv_selection_info_t *selectionInfo, gerbv_image_t *image, struct gerbv_net *net){
109 gdouble corner1X,corner1Y,corner2X,corner2Y;
111 corner1X = selectionInfo->lowerLeftX;
112 corner1Y = selectionInfo->lowerLeftY;
113 corner2X = selectionInfo->upperRightX;
114 corner2Y = selectionInfo->upperRightY;
116 /* calculate the coordinate of the user's click in the current
117 transformation matrix */
118 cairo_device_to_user (cairoTarget, &corner1X, &corner1Y);
119 cairo_device_to_user (cairoTarget, &corner2X, &corner2Y);
120 if (selectionInfo->type == GERBV_SELECTION_POINT_CLICK) {
121 /* use the cairo in_fill routine to see if the point is within the
122 drawn area */
123 if ((isStroke && cairo_in_stroke (cairoTarget, corner1X, corner1Y)) ||
124 (!isStroke && cairo_in_fill (cairoTarget, corner1X, corner1Y))) {
125 /* add the net to the selection array */
126 if (!draw_net_in_selection_buffer(net, selectionInfo)) {
127 gerbv_selection_item_t sItem = {image, net};
128 g_array_append_val (selectionInfo->selectedNodeArray, sItem);
132 else if (selectionInfo->type == GERBV_SELECTION_DRAG_BOX) {
133 gdouble x1,x2,y1,y2;
134 gdouble minX,minY,maxX,maxY;
136 /* we can't assume the "lowerleft" corner is actually in the lower left,
137 since the cairo transformation matrix may be mirrored,etc */
138 minX = MIN(corner1X,corner2X);
139 maxX = MAX(corner1X,corner2X);
140 minY = MIN(corner1Y,corner2Y);
141 maxY = MAX(corner1Y,corner2Y);
142 if (isStroke)
143 cairo_stroke_extents (cairoTarget, &x1, &y1, &x2, &y2);
144 else
145 cairo_fill_extents (cairoTarget, &x1, &y1, &x2, &y2);
147 if ((minX < x1) && (minY < y1) && (maxX > x2) && (maxY > y2)) {
148 /* add the net to the selection array */
149 if (!draw_net_in_selection_buffer(net, selectionInfo)) {
150 gerbv_selection_item_t sItem = {image, net};
151 g_array_append_val (selectionInfo->selectedNodeArray, sItem);
155 /* clear the path, since we didn't actually draw it and cairo
156 doesn't reset it after the previous calls */
157 cairo_new_path (cairoTarget);
160 static void
161 draw_fill (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
162 gerbv_image_t *image, struct gerbv_net *net){
163 if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
164 cairo_fill (cairoTarget);
165 else
166 draw_check_if_object_is_in_selected_area (cairoTarget, FALSE,
167 selectionInfo, image, net);
170 static void
171 draw_stroke (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
172 gerbv_image_t *image, struct gerbv_net *net){
173 if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
174 cairo_stroke (cairoTarget);
175 else
176 draw_check_if_object_is_in_selected_area (cairoTarget, TRUE,
177 selectionInfo, image, net);
181 * Draws a circle _centered_ at x,y with diameter dia
183 static void
184 gerbv_draw_circle(cairo_t *cairoTarget, gdouble diameter)
186 cairo_arc (cairoTarget, 0.0, 0.0, diameter/2.0, 0, 2.0*M_PI);
187 return;
188 } /* gerbv_draw_circle */
192 * Draws a rectangle _centered_ at x,y with sides x_side, y_side
194 static void
195 gerbv_draw_rectangle(cairo_t *cairoTarget, gdouble width1, gdouble height1, gboolean pixelOutput)
197 gdouble width = width1, height = height1;
198 if (pixelOutput) {
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, gboolean pixelOutput)
272 if (dimensionX) {
273 if (dimensionY) {
274 gerbv_draw_rectangle (cairoTarget, dimensionX, dimensionY, pixelOutput);
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, gboolean pixelOutput){
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, pixelOutput);
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, pixelOutput);
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 pixelOutput){
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;
594 gboolean limitLineWidth = TRUE;
595 gboolean displayPixel = TRUE;
596 // if we are scaling the image at all, ignore the line width checks since scaled up
597 // lines can still be visible
598 if ((scaleX != 1)||(scaleY != 1)){
599 limitLineWidth = FALSE;
602 if (transform.mirrorAroundX)
603 scaleY *= -1;
604 if (transform.mirrorAroundY)
605 scaleX *= -1;
606 cairo_translate (cairoTarget, transform.translateX, transform.translateY);
607 cairo_scale (cairoTarget, scaleX, scaleY);
608 cairo_rotate (cairoTarget, transform.rotation);
610 gboolean useOptimizations = allowOptimization;
611 // if the user is using any transformations for this layer, then don't bother using rendering
612 // optimizations
613 if ((fabs(transform.translateX) > 0.00001) ||
614 (fabs(transform.translateY) > 0.00001) ||
615 (fabs(transform.scaleX - 1) > 0.00001) ||
616 (fabs(transform.scaleY - 1) > 0.00001) ||
617 (fabs(transform.rotation) > 0.00001) ||
618 transform.mirrorAroundX || transform.mirrorAroundY)
619 useOptimizations = FALSE;
621 if (useOptimizations && pixelOutput) {
622 minX = renderInfo->lowerLeftX;
623 minY = renderInfo->lowerLeftY;
624 maxX = renderInfo->lowerLeftX + (renderInfo->displayWidth /
625 renderInfo->scaleFactorX);
626 maxY = renderInfo->lowerLeftY + (renderInfo->displayHeight /
627 renderInfo->scaleFactorY);
630 /* do initial justify */
631 cairo_translate (cairoTarget, image->info->imageJustifyOffsetActualA,
632 image->info->imageJustifyOffsetActualB);
634 /* set the fill rule so aperture holes are cleared correctly */
635 cairo_set_fill_rule (cairoTarget, CAIRO_FILL_RULE_EVEN_ODD);
636 /* offset image */
637 cairo_translate (cairoTarget, image->info->offsetA, image->info->offsetB);
638 /* do image rotation */
639 cairo_rotate (cairoTarget, image->info->imageRotation);
640 /* load in polarity operators depending on the image polarity */
641 invertPolarity = transform.inverted;
642 if (image->info->polarity == GERBV_POLARITY_NEGATIVE)
643 invertPolarity = !invertPolarity;
644 if (drawMode == DRAW_SELECTIONS)
645 invertPolarity = FALSE;
647 if (invertPolarity) {
648 drawOperatorClear = CAIRO_OPERATOR_OVER;
649 drawOperatorDark = CAIRO_OPERATOR_CLEAR;
650 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
651 cairo_paint (cairoTarget);
652 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
654 else {
655 drawOperatorClear = CAIRO_OPERATOR_CLEAR;
656 drawOperatorDark = CAIRO_OPERATOR_OVER;
658 /* next, push two cairo states to simulate the first layer and netstate
659 translations (these will be popped when another layer or netstate is
660 started */
662 cairo_save (cairoTarget);
663 cairo_save (cairoTarget);
664 /* store the current layer and netstate so we know when they change */
665 oldLayer = image->layers;
666 oldState = image->states;
668 for (net = image->netlist->next ; net != NULL; net = gerbv_image_return_next_renderable_object(net)) {
670 /* check if this is a new layer */
671 if (net->layer != oldLayer){
672 /* it's a new layer, so recalculate the new transformation matrix
673 for it */
674 cairo_restore (cairoTarget);
675 cairo_restore (cairoTarget);
676 cairo_save (cairoTarget);
677 /* do any rotations */
678 cairo_rotate (cairoTarget, net->layer->rotation);
679 /* handle the layer polarity */
680 if ((net->layer->polarity == GERBV_POLARITY_CLEAR)^invertPolarity) {
681 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
682 drawOperatorClear = CAIRO_OPERATOR_OVER;
683 drawOperatorDark = CAIRO_OPERATOR_CLEAR;
685 else {
686 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
687 drawOperatorClear = CAIRO_OPERATOR_CLEAR;
688 drawOperatorDark = CAIRO_OPERATOR_OVER;
690 /* check for changes to step and repeat */
691 repeat_X = net->layer->stepAndRepeat.X;
692 repeat_Y = net->layer->stepAndRepeat.Y;
693 repeat_dist_X = net->layer->stepAndRepeat.dist_X;
694 repeat_dist_Y = net->layer->stepAndRepeat.dist_Y;
695 /* draw any knockout areas */
696 if (net->layer->knockout.firstInstance == TRUE) {
697 cairo_operator_t oldOperator = cairo_get_operator (cairoTarget);
698 if (net->layer->knockout.polarity == GERBV_POLARITY_CLEAR) {
699 cairo_set_operator (cairoTarget, drawOperatorClear);
701 else {
702 cairo_set_operator (cairoTarget, drawOperatorDark);
704 cairo_new_path (cairoTarget);
705 cairo_rectangle (cairoTarget, net->layer->knockout.lowerLeftX - net->layer->knockout.border,
706 net->layer->knockout.lowerLeftY - net->layer->knockout.border,
707 net->layer->knockout.width + (net->layer->knockout.border*2),
708 net->layer->knockout.height + (net->layer->knockout.border*2));
709 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
710 cairo_set_operator (cairoTarget, oldOperator);
712 /* finally, reapply old netstate transformation */
713 cairo_save (cairoTarget);
714 draw_apply_netstate_transformation (cairoTarget, net->state);
715 oldLayer = net->layer;
717 /* check if this is a new netstate */
718 if (net->state != oldState){
719 /* pop the transformation matrix back to the "pre-state" state and
720 resave it */
721 cairo_restore (cairoTarget);
722 cairo_save (cairoTarget);
723 /* it's a new state, so recalculate the new transformation matrix
724 for it */
725 draw_apply_netstate_transformation (cairoTarget, net->state);
726 oldState = net->state;
728 /* if we are only drawing from the selection buffer, search if this net is
729 in the buffer */
730 if (drawMode == DRAW_SELECTIONS) {
731 /* this flag makes sure we don't draw any unintentional polygons...
732 if we've successfully entered a polygon (the first net matches, and
733 we don't want to check the nets inside the polygon) then
734 polygonStartNet will be set */
735 if (!polygonStartNet) {
736 if (!draw_net_in_selection_buffer(net, selectionInfo))
737 continue;
740 for(repeat_i = 0; repeat_i < repeat_X; repeat_i++) {
741 for(repeat_j = 0; repeat_j < repeat_Y; repeat_j++) {
742 double sr_x = repeat_i * repeat_dist_X;
743 double sr_y = repeat_j * repeat_dist_Y;
746 if ((useOptimizations && pixelOutput) &&
747 ((net->boundingBox.right+sr_x < minX)
748 || (net->boundingBox.left+sr_x > maxX)
749 || (net->boundingBox.top+sr_y < minY)
750 || (net->boundingBox.bottom+sr_y > maxY))) {
751 continue;
754 x1 = net->start_x + sr_x;
755 y1 = net->start_y + sr_y;
756 x2 = net->stop_x + sr_x;
757 y2 = net->stop_y + sr_y;
759 /* translate circular x,y data as well */
760 if (net->cirseg) {
761 cp_x = net->cirseg->cp_x + sr_x;
762 cp_y = net->cirseg->cp_y + sr_y;
765 /* render any labels attached to this net */
766 /* NOTE: this is currently only used on PNP files, so we may
767 make some assumptions here... */
768 if (net->label) {
769 cairo_set_font_size (cairoTarget, 0.05);
770 cairo_save (cairoTarget);
772 cairo_move_to (cairoTarget, x1, y1);
773 cairo_scale (cairoTarget, 1, -1);
774 cairo_show_text (cairoTarget, net->label->str);
775 cairo_restore (cairoTarget);
778 * Polygon Area Fill routines
780 switch (net->interpolation) {
781 case GERBV_INTERPOLATION_PAREA_START :
782 draw_render_polygon_object (net, cairoTarget, sr_x, sr_y, image,
783 drawMode, selectionInfo, pixelOutput);
784 continue;
785 case GERBV_INTERPOLATION_DELETED:
786 continue;
787 default :
788 break;
792 * If aperture state is off we allow use of undefined apertures.
793 * This happens when gerber files starts, but hasn't decided on
794 * which aperture to use.
796 if (image->aperture[net->aperture] == NULL) {
797 /* Commenting this out since it gets emitted every time you click on the screen
798 if (net->aperture_state != GERBV_APERTURE_STATE_OFF)
799 GERB_MESSAGE("Aperture D%d is not defined\n", net->aperture);
801 continue;
803 switch (net->aperture_state) {
804 case GERBV_APERTURE_STATE_ON :
805 /* if the aperture width is truly 0, then render as a 1 pixel width
806 line. 0 diameter apertures are used by some programs to draw labels,
807 etc, and they are rendered by other programs as 1 pixel wide */
808 /* NOTE: also, make sure all lines are at least 1 pixel wide, so they
809 always show up at low zoom levels */
811 if (limitLineWidth&&((image->aperture[net->aperture]->parameter[0] < pixelWidth)&&
812 (pixelOutput)))
813 criticalRadius = pixelWidth/2.0;
814 else
815 criticalRadius = image->aperture[net->aperture]->parameter[0]/2.0;
816 lineWidth = criticalRadius*2.0;
817 // convert to a pixel integer
818 cairo_user_to_device_distance (cairoTarget, &lineWidth, &x1);
819 if (pixelOutput) {
820 lineWidth = round(lineWidth);
821 if ((int)lineWidth % 2) {
822 oddWidth = TRUE;
824 else {
825 oddWidth = FALSE;
828 cairo_device_to_user_distance (cairoTarget, &lineWidth, &x1);
829 cairo_set_line_width (cairoTarget, lineWidth);
830 switch (net->interpolation) {
831 case GERBV_INTERPOLATION_x10 :
832 case GERBV_INTERPOLATION_LINEARx01 :
833 case GERBV_INTERPOLATION_LINEARx001 :
834 case GERBV_INTERPOLATION_LINEARx1 :
835 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
836 // weed out any lines that are obviously not going to render on the
837 // visible screen
839 switch (image->aperture[net->aperture]->type) {
840 case GERBV_APTYPE_CIRCLE :
841 draw_cairo_move_to (cairoTarget, x1, y1, oddWidth, pixelOutput);
842 draw_cairo_line_to (cairoTarget, x2, y2, oddWidth, pixelOutput);
843 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
844 break;
845 case GERBV_APTYPE_RECTANGLE :
846 dx = (image->aperture[net->aperture]->parameter[0]/ 2);
847 dy = (image->aperture[net->aperture]->parameter[1]/ 2);
848 if(x1 > x2)
849 dx = -dx;
850 if(y1 > y2)
851 dy = -dy;
852 cairo_new_path(cairoTarget);
853 draw_cairo_move_to (cairoTarget, x1 - dx, y1 - dy, FALSE, pixelOutput);
854 draw_cairo_line_to (cairoTarget, x1 - dx, y1 + dy, FALSE, pixelOutput);
855 draw_cairo_line_to (cairoTarget, x2 - dx, y2 + dy, FALSE, pixelOutput);
856 draw_cairo_line_to (cairoTarget, x2 + dx, y2 + dy, FALSE, pixelOutput);
857 draw_cairo_line_to (cairoTarget, x2 + dx, y2 - dy, FALSE, pixelOutput);
858 draw_cairo_line_to (cairoTarget, x1 + dx, y1 - dy, FALSE, pixelOutput);
859 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
860 break;
861 /* for now, just render ovals or polygons like a circle */
862 case GERBV_APTYPE_OVAL :
863 case GERBV_APTYPE_POLYGON :
864 draw_cairo_move_to (cairoTarget, x1,y1, oddWidth, pixelOutput);
865 draw_cairo_line_to (cairoTarget, x2,y2, oddWidth, pixelOutput);
866 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
867 break;
868 /* macros can only be flashed, so ignore any that might be here */
869 default :
870 break;
872 break;
873 case GERBV_INTERPOLATION_CW_CIRCULAR :
874 case GERBV_INTERPOLATION_CCW_CIRCULAR :
875 /* cairo doesn't have a function to draw oval arcs, so we must
876 * draw an arc and stretch it by scaling different x and y values
878 cairo_new_path(cairoTarget);
879 if (image->aperture[net->aperture]->type == GERBV_APTYPE_RECTANGLE) {
880 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_SQUARE);
882 else {
883 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
885 cairo_save (cairoTarget);
886 cairo_translate(cairoTarget, cp_x, cp_y);
887 cairo_scale (cairoTarget, net->cirseg->width, net->cirseg->height);
888 if (net->cirseg->angle2 > net->cirseg->angle1) {
889 cairo_arc (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
890 net->cirseg->angle2 * M_PI/180);
892 else {
893 cairo_arc_negative (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
894 net->cirseg->angle2 * M_PI/180);
896 cairo_restore (cairoTarget);
897 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
898 break;
899 default :
900 break;
902 break;
903 case GERBV_APERTURE_STATE_OFF :
904 break;
905 case GERBV_APERTURE_STATE_FLASH :
906 p1 = image->aperture[net->aperture]->parameter[0];
907 p2 = image->aperture[net->aperture]->parameter[1];
908 p3 = image->aperture[net->aperture]->parameter[2];
909 p4 = image->aperture[net->aperture]->parameter[3];
910 p5 = image->aperture[net->aperture]->parameter[4];
912 cairo_save (cairoTarget);
913 draw_cairo_translate_adjust(cairoTarget, x2, y2, pixelOutput);
915 switch (image->aperture[net->aperture]->type) {
916 case GERBV_APTYPE_CIRCLE :
917 gerbv_draw_circle(cairoTarget, p1);
918 gerbv_draw_aperature_hole (cairoTarget, p2, p3, pixelOutput);
919 break;
920 case GERBV_APTYPE_RECTANGLE :
921 // some CAD programs use very thin flashed rectangles to compose
922 // logos/images, so we must make sure those display here
923 displayPixel = pixelOutput;
924 if (limitLineWidth&&((p1 < pixelWidth)&&
925 (pixelOutput))) {
926 p1 = pixelWidth;
927 displayPixel = FALSE;
929 if (limitLineWidth&&((p2 < pixelWidth)&&
930 (pixelOutput))) {
931 p2 = pixelWidth;
932 displayPixel = FALSE;
934 gerbv_draw_rectangle(cairoTarget, p1, p2, displayPixel);
935 gerbv_draw_aperature_hole (cairoTarget, p3, p4, displayPixel);
936 break;
937 case GERBV_APTYPE_OVAL :
938 gerbv_draw_oblong(cairoTarget, p1, p2);
939 gerbv_draw_aperature_hole (cairoTarget, p3, p4, pixelOutput);
940 break;
941 case GERBV_APTYPE_POLYGON :
942 gerbv_draw_polygon(cairoTarget, p1, p2, p3);
943 gerbv_draw_aperature_hole (cairoTarget, p4, p5, pixelOutput);
944 break;
945 case GERBV_APTYPE_MACRO :
946 gerbv_draw_amacro(cairoTarget, drawOperatorClear, drawOperatorDark,
947 image->aperture[net->aperture]->simplified,
948 (int) image->aperture[net->aperture]->parameter[0], pixelWidth,
949 drawMode, selectionInfo, image, net);
950 break;
951 default :
952 GERB_MESSAGE("Unknown aperture type\n");
953 return 0;
955 /* and finally fill the path */
956 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
957 cairo_restore (cairoTarget);
958 break;
959 default:
960 GERB_MESSAGE("Unknown aperture state\n");
961 return 0;
967 /* restore the initial two state saves (one for layer, one for netstate)*/
968 cairo_restore (cairoTarget);
969 cairo_restore (cairoTarget);
971 return 1;