Move _draw_arc() routine from draw.c into HID_DRAW API
[geda-pcb/pcjc2.git] / src / hid / common / draw_helpers.c
blob774c10a99ebf5921b86ca5dbfa3ad29321552e7d
1 #include "global.h"
2 #include "hid.h"
3 #include "hid_draw.h"
4 #include "data.h" /* For global "PCB" variable */
5 #include "polygon.h"
8 static void
9 common_draw_pcb_line (hidGC gc, LineType *line)
11 gui->graphics->set_line_cap (gc, Trace_Cap);
12 if (TEST_FLAG (THINDRAWFLAG, PCB))
13 gui->graphics->set_line_width (gc, 0);
14 else
15 gui->graphics->set_line_width (gc, line->Thickness);
17 gui->graphics->draw_line (gc,
18 line->Point1.X, line->Point1.Y,
19 line->Point2.X, line->Point2.Y);
22 static void
23 common_draw_pcb_arc (hidGC gc, ArcType *arc)
25 if (!arc->Thickness)
26 return;
28 if (TEST_FLAG (THINDRAWFLAG, PCB))
29 gui->graphics->set_line_width (gc, 0);
30 else
31 gui->graphics->set_line_width (gc, arc->Thickness);
32 gui->graphics->set_line_cap (gc, Trace_Cap);
34 gui->graphics->draw_arc (gc, arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta);
37 static void
38 fill_contour (hidGC gc, PLINE *pl)
40 Coord *x, *y, n, i = 0;
41 VNODE *v;
43 n = pl->Count;
44 x = (Coord *)malloc (n * sizeof (*x));
45 y = (Coord *)malloc (n * sizeof (*y));
47 for (v = &pl->head; i < n; v = v->next)
49 x[i] = v->point[0];
50 y[i++] = v->point[1];
53 gui->graphics->fill_polygon (gc, n, x, y);
55 free (x);
56 free (y);
59 static void
60 thindraw_contour (hidGC gc, PLINE *pl)
62 VNODE *v;
63 Coord last_x, last_y;
64 Coord this_x, this_y;
66 gui->graphics->set_line_width (gc, 0);
67 gui->graphics->set_line_cap (gc, Round_Cap);
69 /* If the contour is round, use an arc drawing routine. */
70 if (pl->is_round)
72 gui->graphics->draw_arc (gc, pl->cx, pl->cy, pl->radius, pl->radius, 0, 360);
73 return;
76 /* Need at least two points in the contour */
77 if (pl->head.next == NULL)
78 return;
80 last_x = pl->head.point[0];
81 last_y = pl->head.point[1];
82 v = pl->head.next;
86 this_x = v->point[0];
87 this_y = v->point[1];
89 gui->graphics->draw_line (gc, last_x, last_y, this_x, this_y);
90 // gui->graphics->fill_circle (gc, this_x, this_y, 30);
92 last_x = this_x;
93 last_y = this_y;
95 while ((v = v->next) != pl->head.next);
98 static void
99 fill_contour_cb (PLINE *pl, void *user_data)
101 hidGC gc = (hidGC)user_data;
102 PLINE *local_pl = pl;
104 fill_contour (gc, pl);
105 poly_FreeContours (&local_pl);
108 static void
109 fill_clipped_contour (hidGC gc, PLINE *pl, const BoxType *clip_box)
111 PLINE *pl_copy;
112 POLYAREA *clip_poly;
113 POLYAREA *piece_poly;
114 POLYAREA *clipped_pieces;
115 POLYAREA *draw_piece;
116 int x;
118 clip_poly = RectPoly (clip_box->X1, clip_box->X2,
119 clip_box->Y1, clip_box->Y2);
120 poly_CopyContour (&pl_copy, pl);
121 piece_poly = poly_Create ();
122 poly_InclContour (piece_poly, pl_copy);
123 x = poly_Boolean_free (piece_poly, clip_poly,
124 &clipped_pieces, PBO_ISECT);
125 if (x != err_ok || clipped_pieces == NULL)
126 return;
128 draw_piece = clipped_pieces;
131 /* NB: The polygon won't have any holes in it */
132 fill_contour (gc, draw_piece->contours);
134 while ((draw_piece = draw_piece->f) != clipped_pieces);
135 poly_Free (&clipped_pieces);
138 /* If at least 50% of the bounding box of the polygon is on the screen,
139 * lets compute the complete no-holes polygon.
141 #define BOUNDS_INSIDE_CLIP_THRESHOLD 0.5
142 static int
143 should_compute_no_holes (PolygonType *poly, const BoxType *clip_box)
145 Coord x1, x2, y1, y2;
146 double poly_bounding_area;
147 double clipped_poly_area;
149 /* If there is no passed clip box, compute the whole thing */
150 if (clip_box == NULL)
151 return 1;
153 x1 = MAX (poly->BoundingBox.X1, clip_box->X1);
154 x2 = MIN (poly->BoundingBox.X2, clip_box->X2);
155 y1 = MAX (poly->BoundingBox.Y1, clip_box->Y1);
156 y2 = MIN (poly->BoundingBox.Y2, clip_box->Y2);
158 /* Check if the polygon is outside the clip box */
159 if ((x2 <= x1) || (y2 <= y1))
160 return 0;
162 poly_bounding_area = (double)(poly->BoundingBox.X2 - poly->BoundingBox.X1) *
163 (double)(poly->BoundingBox.Y2 - poly->BoundingBox.Y1);
165 clipped_poly_area = (double)(x2 - x1) * (double)(y2 - y1);
167 if (clipped_poly_area / poly_bounding_area >= BOUNDS_INSIDE_CLIP_THRESHOLD)
168 return 1;
170 return 0;
172 #undef BOUNDS_INSIDE_CLIP_THRESHOLD
174 void
175 common_fill_pcb_polygon (hidGC gc, PolygonType *poly, const BoxType *clip_box)
177 if (!poly->NoHolesValid)
179 /* If enough of the polygon is on-screen, compute the entire
180 * NoHoles version and cache it for later rendering, otherwise
181 * just compute what we need to render now.
183 if (should_compute_no_holes (poly, clip_box))
184 ComputeNoHoles (poly);
185 else
186 NoHolesPolygonDicer (poly, clip_box, fill_contour_cb, gc);
188 if (poly->NoHolesValid && poly->NoHoles)
190 PLINE *pl;
192 for (pl = poly->NoHoles; pl != NULL; pl = pl->next)
194 if (clip_box == NULL)
195 fill_contour (gc, pl);
196 else
197 fill_clipped_contour (gc, pl, clip_box);
201 /* Draw other parts of the polygon if fullpoly flag is set */
202 /* NB: No "NoHoles" cache for these */
203 if (TEST_FLAG (FULLPOLYFLAG, poly))
205 PolygonType p = *poly;
207 for (p.Clipped = poly->Clipped->f;
208 p.Clipped != poly->Clipped;
209 p.Clipped = p.Clipped->f)
210 NoHolesPolygonDicer (&p, clip_box, fill_contour_cb, gc);
214 static int
215 thindraw_hole_cb (PLINE *pl, void *user_data)
217 hidGC gc = (hidGC)user_data;
218 thindraw_contour (gc, pl);
219 return 0;
222 void
223 common_thindraw_pcb_polygon (hidGC gc, PolygonType *poly,
224 const BoxType *clip_box)
226 thindraw_contour (gc, poly->Clipped->contours);
227 PolygonHoles (poly, clip_box, thindraw_hole_cb, gc);
230 void
231 common_thindraw_pcb_pad (hidGC gc, PadType *pad, bool clear, bool mask)
233 Coord w = clear ? (mask ? pad->Mask
234 : pad->Thickness + pad->Clearance)
235 : pad->Thickness;
236 Coord x1, y1, x2, y2;
237 Coord t = w / 2;
238 x1 = pad->Point1.X;
239 y1 = pad->Point1.Y;
240 x2 = pad->Point2.X;
241 y2 = pad->Point2.Y;
242 if (x1 > x2 || y1 > y2)
244 Coord temp_x = x1;
245 Coord temp_y = y1;
246 x1 = x2; x2 = temp_x;
247 y1 = y2; y2 = temp_y;
249 gui->graphics->set_line_cap (gc, Round_Cap);
250 gui->graphics->set_line_width (gc, 0);
251 if (TEST_FLAG (SQUAREFLAG, pad))
253 /* slanted square pad */
254 double tx, ty, theta;
256 if (x1 == x2 && y1 == y2)
257 theta = 0;
258 else
259 theta = atan2 (y2 - y1, x2 - x1);
261 /* T is a vector half a thickness long, in the direction of
262 one of the corners. */
263 tx = t * cos (theta + M_PI / 4) * sqrt (2.0);
264 ty = t * sin (theta + M_PI / 4) * sqrt (2.0);
266 gui->graphics->draw_line (gc, x1 - tx, y1 - ty, x2 + ty, y2 - tx);
267 gui->graphics->draw_line (gc, x2 + ty, y2 - tx, x2 + tx, y2 + ty);
268 gui->graphics->draw_line (gc, x2 + tx, y2 + ty, x1 - ty, y1 + tx);
269 gui->graphics->draw_line (gc, x1 - ty, y1 + tx, x1 - tx, y1 - ty);
271 else if (x1 == x2 && y1 == y2)
273 gui->graphics->draw_arc (gc, x1, y1, t, t, 0, 360);
275 else
277 /* Slanted round-end pads. */
278 Coord dx, dy, ox, oy;
279 double h;
281 dx = x2 - x1;
282 dy = y2 - y1;
283 h = t / sqrt (SQUARE (dx) + SQUARE (dy));
284 ox = dy * h + 0.5 * SGN (dy);
285 oy = -(dx * h + 0.5 * SGN (dx));
287 gui->graphics->draw_line (gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
289 if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
291 Angle angle = atan2 (dx, dy) * 57.295779;
292 gui->graphics->draw_line (gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
293 gui->graphics->draw_arc (gc, x1, y1, t, t, angle - 180, 180);
294 gui->graphics->draw_arc (gc, x2, y2, t, t, angle, 180);
299 void
300 common_fill_pcb_pad (hidGC gc, PadType *pad, bool clear, bool mask)
302 Coord w = clear ? (mask ? pad->Mask
303 : pad->Thickness + pad->Clearance)
304 : pad->Thickness;
306 if (pad->Point1.X == pad->Point2.X &&
307 pad->Point1.Y == pad->Point2.Y)
309 if (TEST_FLAG (SQUAREFLAG, pad))
311 Coord l, r, t, b;
312 l = pad->Point1.X - w / 2;
313 b = pad->Point1.Y - w / 2;
314 r = l + w;
315 t = b + w;
316 gui->graphics->fill_rect (gc, l, b, r, t);
318 else
320 gui->graphics->fill_circle (gc, pad->Point1.X, pad->Point1.Y, w / 2);
323 else
325 gui->graphics->set_line_cap (gc, TEST_FLAG (SQUAREFLAG, pad) ?
326 Square_Cap : Round_Cap);
327 gui->graphics->set_line_width (gc, w);
329 gui->graphics->draw_line (gc, pad->Point1.X, pad->Point1.Y,
330 pad->Point2.X, pad->Point2.Y);
334 /* ---------------------------------------------------------------------------
335 * draws one polygon
336 * x and y are already in display coordinates
337 * the points are numbered:
339 * 5 --- 6
340 * / \
341 * 4 7
342 * | |
343 * 3 0
344 * \ /
345 * 2 --- 1
348 typedef struct
350 double X, Y;
352 FloatPolyType;
354 static void
355 draw_octagon_poly (hidGC gc, Coord X, Coord Y,
356 Coord Thickness, bool thin_draw)
358 static FloatPolyType p[8] = {
359 { 0.5, -TAN_22_5_DEGREE_2},
360 { TAN_22_5_DEGREE_2, -0.5 },
361 {-TAN_22_5_DEGREE_2, -0.5 },
362 {-0.5, -TAN_22_5_DEGREE_2},
363 {-0.5, TAN_22_5_DEGREE_2},
364 {-TAN_22_5_DEGREE_2, 0.5 },
365 { TAN_22_5_DEGREE_2, 0.5 },
366 { 0.5, TAN_22_5_DEGREE_2}
368 static int special_size = 0;
369 static int scaled_x[8];
370 static int scaled_y[8];
371 Coord polygon_x[9];
372 Coord polygon_y[9];
373 int i;
375 if (Thickness != special_size)
377 special_size = Thickness;
378 for (i = 0; i < 8; i++)
380 scaled_x[i] = p[i].X * special_size;
381 scaled_y[i] = p[i].Y * special_size;
384 /* add line offset */
385 for (i = 0; i < 8; i++)
387 polygon_x[i] = X + scaled_x[i];
388 polygon_y[i] = Y + scaled_y[i];
391 if (thin_draw)
393 int i;
394 gui->graphics->set_line_cap (gc, Round_Cap);
395 gui->graphics->set_line_width (gc, 0);
396 polygon_x[8] = X + scaled_x[0];
397 polygon_y[8] = Y + scaled_y[0];
398 for (i = 0; i < 8; i++)
399 gui->graphics->draw_line (gc, polygon_x[i ], polygon_y[i ],
400 polygon_x[i + 1], polygon_y[i + 1]);
402 else
403 gui->graphics->fill_polygon (gc, 8, polygon_x, polygon_y);
406 void
407 common_fill_pcb_pv (hidGC fg_gc, hidGC bg_gc, PinType *pv, bool drawHole, bool mask)
409 Coord w = mask ? pv->Mask : pv->Thickness;
410 Coord r = w / 2;
412 if (TEST_FLAG (HOLEFLAG, pv))
414 if (mask)
415 gui->graphics->fill_circle (bg_gc, pv->X, pv->Y, r);
416 if (drawHole)
418 gui->graphics->fill_circle (bg_gc, pv->X, pv->Y, r);
419 gui->graphics->set_line_cap (fg_gc, Round_Cap);
420 gui->graphics->set_line_width (fg_gc, 0);
421 gui->graphics->draw_arc (fg_gc, pv->X, pv->Y, r, r, 0, 360);
423 return;
426 if (TEST_FLAG (SQUAREFLAG, pv))
428 Coord l = pv->X - r;
429 Coord b = pv->Y - r;
430 Coord r = l + w;
431 Coord t = b + w;
433 gui->graphics->fill_rect (fg_gc, l, b, r, t);
435 else if (TEST_FLAG (OCTAGONFLAG, pv))
436 draw_octagon_poly (fg_gc, pv->X, pv->Y, w, false);
437 else /* draw a round pin or via */
438 gui->graphics->fill_circle (fg_gc, pv->X, pv->Y, r);
440 /* and the drilling hole (which is always round) */
441 if (drawHole)
442 gui->graphics->fill_circle (bg_gc, pv->X, pv->Y, pv->DrillingHole / 2);
445 void
446 common_thindraw_pcb_pv (hidGC fg_gc, hidGC bg_gc, PinType *pv, bool drawHole, bool mask)
448 Coord w = mask ? pv->Mask : pv->Thickness;
449 Coord r = w / 2;
451 if (TEST_FLAG (HOLEFLAG, pv))
453 if (mask)
454 gui->graphics->draw_arc (fg_gc, pv->X, pv->Y, r, r, 0, 360);
455 if (drawHole)
457 r = pv->DrillingHole / 2;
458 gui->graphics->set_line_cap (bg_gc, Round_Cap);
459 gui->graphics->set_line_width (bg_gc, 0);
460 gui->graphics->draw_arc (bg_gc, pv->X, pv->Y, r, r, 0, 360);
462 return;
465 if (TEST_FLAG (SQUAREFLAG, pv))
467 Coord l = pv->X - r;
468 Coord b = pv->Y - r;
469 Coord r = l + w;
470 Coord t = b + w;
472 gui->graphics->set_line_cap (fg_gc, Round_Cap);
473 gui->graphics->set_line_width (fg_gc, 0);
474 gui->graphics->draw_line (fg_gc, r, t, r, b);
475 gui->graphics->draw_line (fg_gc, l, t, l, b);
476 gui->graphics->draw_line (fg_gc, r, t, l, t);
477 gui->graphics->draw_line (fg_gc, r, b, l, b);
480 else if (TEST_FLAG (OCTAGONFLAG, pv))
482 draw_octagon_poly (fg_gc, pv->X, pv->Y, w, true);
484 else /* draw a round pin or via */
486 gui->graphics->set_line_cap (fg_gc, Round_Cap);
487 gui->graphics->set_line_width (fg_gc, 0);
488 gui->graphics->draw_arc (fg_gc, pv->X, pv->Y, r, r, 0, 360);
491 /* and the drilling hole (which is always round */
492 if (drawHole)
494 gui->graphics->set_line_cap (bg_gc, Round_Cap);
495 gui->graphics->set_line_width (bg_gc, 0);
496 gui->graphics->draw_arc (bg_gc, pv->X, pv->Y, pv->DrillingHole / 2,
497 pv->DrillingHole / 2, 0, 360);
501 void
502 common_draw_helpers_init (HID_DRAW *graphics)
504 graphics->draw_pcb_line = common_draw_pcb_line;
505 graphics->draw_pcb_arc = common_draw_pcb_arc;
507 graphics->fill_pcb_polygon = common_fill_pcb_polygon;
508 graphics->thindraw_pcb_polygon = common_thindraw_pcb_polygon;
509 graphics->fill_pcb_pad = common_fill_pcb_pad;
510 graphics->thindraw_pcb_pad = common_thindraw_pcb_pad;
511 graphics->fill_pcb_pv = common_fill_pcb_pv;
512 graphics->thindraw_pcb_pv = common_thindraw_pcb_pv;