Move DrawTextLowlevel() routine from draw.c into the HID_DRAW API
[geda-pcb/pcjc2.git] / src / hid / common / draw_helpers.c
blobf6fbe71afb9920659a299fe4908c84c0a342eefd
1 #include "global.h"
2 #include "hid.h"
3 #include "hid_draw.h"
4 #include "data.h" /* For global "PCB" variable */
5 #include "rotate.h" /* For RotateLineLowLevel() */
6 #include "polygon.h"
9 static void
10 common_draw_pcb_line (hidGC gc, LineType *line)
12 gui->graphics->set_line_cap (gc, Trace_Cap);
13 if (TEST_FLAG (THINDRAWFLAG, PCB))
14 gui->graphics->set_line_width (gc, 0);
15 else
16 gui->graphics->set_line_width (gc, line->Thickness);
18 gui->graphics->draw_line (gc,
19 line->Point1.X, line->Point1.Y,
20 line->Point2.X, line->Point2.Y);
23 static void
24 common_draw_pcb_arc (hidGC gc, ArcType *arc)
26 if (!arc->Thickness)
27 return;
29 if (TEST_FLAG (THINDRAWFLAG, PCB))
30 gui->graphics->set_line_width (gc, 0);
31 else
32 gui->graphics->set_line_width (gc, arc->Thickness);
33 gui->graphics->set_line_cap (gc, Trace_Cap);
35 gui->graphics->draw_arc (gc, arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle, arc->Delta);
38 /* ---------------------------------------------------------------------------
39 * drawing routine for text objects
41 static void
42 common_draw_pcb_text (hidGC gc, TextType *Text, Coord min_line_width)
44 Coord x = 0;
45 unsigned char *string = (unsigned char *) Text->TextString;
46 Cardinal n;
47 FontType *font = &PCB->Font;
49 while (string && *string)
51 /* draw lines if symbol is valid and data is present */
52 if (*string <= MAX_FONTPOSITION && font->Symbol[*string].Valid)
54 LineType *line = font->Symbol[*string].Line;
55 LineType newline;
57 for (n = font->Symbol[*string].LineN; n; n--, line++)
59 /* create one line, scale, move, rotate and swap it */
60 newline = *line;
61 newline.Point1.X = SCALE_TEXT (newline.Point1.X + x, Text->Scale);
62 newline.Point1.Y = SCALE_TEXT (newline.Point1.Y, Text->Scale);
63 newline.Point2.X = SCALE_TEXT (newline.Point2.X + x, Text->Scale);
64 newline.Point2.Y = SCALE_TEXT (newline.Point2.Y, Text->Scale);
65 newline.Thickness = SCALE_TEXT (newline.Thickness, Text->Scale / 2);
66 if (newline.Thickness < min_line_width)
67 newline.Thickness = min_line_width;
69 RotateLineLowLevel (&newline, 0, 0, Text->Direction);
71 /* the labels of SMD objects on the bottom
72 * side haven't been swapped yet, only their offset
74 if (TEST_FLAG (ONSOLDERFLAG, Text))
76 newline.Point1.X = SWAP_SIGN_X (newline.Point1.X);
77 newline.Point1.Y = SWAP_SIGN_Y (newline.Point1.Y);
78 newline.Point2.X = SWAP_SIGN_X (newline.Point2.X);
79 newline.Point2.Y = SWAP_SIGN_Y (newline.Point2.Y);
81 /* add offset and draw line */
82 newline.Point1.X += Text->X;
83 newline.Point1.Y += Text->Y;
84 newline.Point2.X += Text->X;
85 newline.Point2.Y += Text->Y;
86 gui->graphics->draw_pcb_line (gc, &newline);
89 /* move on to next cursor position */
90 x += (font->Symbol[*string].Width + font->Symbol[*string].Delta);
92 else
94 /* the default symbol is a filled box */
95 BoxType defaultsymbol = PCB->Font.DefaultSymbol;
96 Coord size = (defaultsymbol.X2 - defaultsymbol.X1) * 6 / 5;
98 defaultsymbol.X1 = SCALE_TEXT (defaultsymbol.X1 + x, Text->Scale);
99 defaultsymbol.Y1 = SCALE_TEXT (defaultsymbol.Y1, Text->Scale);
100 defaultsymbol.X2 = SCALE_TEXT (defaultsymbol.X2 + x, Text->Scale);
101 defaultsymbol.Y2 = SCALE_TEXT (defaultsymbol.Y2, Text->Scale);
103 RotateBoxLowLevel (&defaultsymbol, 0, 0, Text->Direction);
105 /* add offset and draw box */
106 defaultsymbol.X1 += Text->X;
107 defaultsymbol.Y1 += Text->Y;
108 defaultsymbol.X2 += Text->X;
109 defaultsymbol.Y2 += Text->Y;
110 gui->graphics->fill_rect (gc,
111 defaultsymbol.X1, defaultsymbol.Y1,
112 defaultsymbol.X2, defaultsymbol.Y2);
114 /* move on to next cursor position */
115 x += size;
117 string++;
121 static void
122 fill_contour (hidGC gc, PLINE *pl)
124 Coord *x, *y, n, i = 0;
125 VNODE *v;
127 n = pl->Count;
128 x = (Coord *)malloc (n * sizeof (*x));
129 y = (Coord *)malloc (n * sizeof (*y));
131 for (v = &pl->head; i < n; v = v->next)
133 x[i] = v->point[0];
134 y[i++] = v->point[1];
137 gui->graphics->fill_polygon (gc, n, x, y);
139 free (x);
140 free (y);
143 static void
144 thindraw_contour (hidGC gc, PLINE *pl)
146 VNODE *v;
147 Coord last_x, last_y;
148 Coord this_x, this_y;
150 gui->graphics->set_line_width (gc, 0);
151 gui->graphics->set_line_cap (gc, Round_Cap);
153 /* If the contour is round, use an arc drawing routine. */
154 if (pl->is_round)
156 gui->graphics->draw_arc (gc, pl->cx, pl->cy, pl->radius, pl->radius, 0, 360);
157 return;
160 /* Need at least two points in the contour */
161 if (pl->head.next == NULL)
162 return;
164 last_x = pl->head.point[0];
165 last_y = pl->head.point[1];
166 v = pl->head.next;
170 this_x = v->point[0];
171 this_y = v->point[1];
173 gui->graphics->draw_line (gc, last_x, last_y, this_x, this_y);
174 // gui->graphics->fill_circle (gc, this_x, this_y, 30);
176 last_x = this_x;
177 last_y = this_y;
179 while ((v = v->next) != pl->head.next);
182 static void
183 fill_contour_cb (PLINE *pl, void *user_data)
185 hidGC gc = (hidGC)user_data;
186 PLINE *local_pl = pl;
188 fill_contour (gc, pl);
189 poly_FreeContours (&local_pl);
192 static void
193 fill_clipped_contour (hidGC gc, PLINE *pl, const BoxType *clip_box)
195 PLINE *pl_copy;
196 POLYAREA *clip_poly;
197 POLYAREA *piece_poly;
198 POLYAREA *clipped_pieces;
199 POLYAREA *draw_piece;
200 int x;
202 clip_poly = RectPoly (clip_box->X1, clip_box->X2,
203 clip_box->Y1, clip_box->Y2);
204 poly_CopyContour (&pl_copy, pl);
205 piece_poly = poly_Create ();
206 poly_InclContour (piece_poly, pl_copy);
207 x = poly_Boolean_free (piece_poly, clip_poly,
208 &clipped_pieces, PBO_ISECT);
209 if (x != err_ok || clipped_pieces == NULL)
210 return;
212 draw_piece = clipped_pieces;
215 /* NB: The polygon won't have any holes in it */
216 fill_contour (gc, draw_piece->contours);
218 while ((draw_piece = draw_piece->f) != clipped_pieces);
219 poly_Free (&clipped_pieces);
222 /* If at least 50% of the bounding box of the polygon is on the screen,
223 * lets compute the complete no-holes polygon.
225 #define BOUNDS_INSIDE_CLIP_THRESHOLD 0.5
226 static int
227 should_compute_no_holes (PolygonType *poly, const BoxType *clip_box)
229 Coord x1, x2, y1, y2;
230 double poly_bounding_area;
231 double clipped_poly_area;
233 /* If there is no passed clip box, compute the whole thing */
234 if (clip_box == NULL)
235 return 1;
237 x1 = MAX (poly->BoundingBox.X1, clip_box->X1);
238 x2 = MIN (poly->BoundingBox.X2, clip_box->X2);
239 y1 = MAX (poly->BoundingBox.Y1, clip_box->Y1);
240 y2 = MIN (poly->BoundingBox.Y2, clip_box->Y2);
242 /* Check if the polygon is outside the clip box */
243 if ((x2 <= x1) || (y2 <= y1))
244 return 0;
246 poly_bounding_area = (double)(poly->BoundingBox.X2 - poly->BoundingBox.X1) *
247 (double)(poly->BoundingBox.Y2 - poly->BoundingBox.Y1);
249 clipped_poly_area = (double)(x2 - x1) * (double)(y2 - y1);
251 if (clipped_poly_area / poly_bounding_area >= BOUNDS_INSIDE_CLIP_THRESHOLD)
252 return 1;
254 return 0;
256 #undef BOUNDS_INSIDE_CLIP_THRESHOLD
258 void
259 common_fill_pcb_polygon (hidGC gc, PolygonType *poly, const BoxType *clip_box)
261 if (!poly->NoHolesValid)
263 /* If enough of the polygon is on-screen, compute the entire
264 * NoHoles version and cache it for later rendering, otherwise
265 * just compute what we need to render now.
267 if (should_compute_no_holes (poly, clip_box))
268 ComputeNoHoles (poly);
269 else
270 NoHolesPolygonDicer (poly, clip_box, fill_contour_cb, gc);
272 if (poly->NoHolesValid && poly->NoHoles)
274 PLINE *pl;
276 for (pl = poly->NoHoles; pl != NULL; pl = pl->next)
278 if (clip_box == NULL)
279 fill_contour (gc, pl);
280 else
281 fill_clipped_contour (gc, pl, clip_box);
285 /* Draw other parts of the polygon if fullpoly flag is set */
286 /* NB: No "NoHoles" cache for these */
287 if (TEST_FLAG (FULLPOLYFLAG, poly))
289 PolygonType p = *poly;
291 for (p.Clipped = poly->Clipped->f;
292 p.Clipped != poly->Clipped;
293 p.Clipped = p.Clipped->f)
294 NoHolesPolygonDicer (&p, clip_box, fill_contour_cb, gc);
298 static int
299 thindraw_hole_cb (PLINE *pl, void *user_data)
301 hidGC gc = (hidGC)user_data;
302 thindraw_contour (gc, pl);
303 return 0;
306 void
307 common_thindraw_pcb_polygon (hidGC gc, PolygonType *poly,
308 const BoxType *clip_box)
310 thindraw_contour (gc, poly->Clipped->contours);
311 PolygonHoles (poly, clip_box, thindraw_hole_cb, gc);
314 void
315 common_thindraw_pcb_pad (hidGC gc, PadType *pad, bool clear, bool mask)
317 Coord w = clear ? (mask ? pad->Mask
318 : pad->Thickness + pad->Clearance)
319 : pad->Thickness;
320 Coord x1, y1, x2, y2;
321 Coord t = w / 2;
322 x1 = pad->Point1.X;
323 y1 = pad->Point1.Y;
324 x2 = pad->Point2.X;
325 y2 = pad->Point2.Y;
326 if (x1 > x2 || y1 > y2)
328 Coord temp_x = x1;
329 Coord temp_y = y1;
330 x1 = x2; x2 = temp_x;
331 y1 = y2; y2 = temp_y;
333 gui->graphics->set_line_cap (gc, Round_Cap);
334 gui->graphics->set_line_width (gc, 0);
335 if (TEST_FLAG (SQUAREFLAG, pad))
337 /* slanted square pad */
338 double tx, ty, theta;
340 if (x1 == x2 && y1 == y2)
341 theta = 0;
342 else
343 theta = atan2 (y2 - y1, x2 - x1);
345 /* T is a vector half a thickness long, in the direction of
346 one of the corners. */
347 tx = t * cos (theta + M_PI / 4) * sqrt (2.0);
348 ty = t * sin (theta + M_PI / 4) * sqrt (2.0);
350 gui->graphics->draw_line (gc, x1 - tx, y1 - ty, x2 + ty, y2 - tx);
351 gui->graphics->draw_line (gc, x2 + ty, y2 - tx, x2 + tx, y2 + ty);
352 gui->graphics->draw_line (gc, x2 + tx, y2 + ty, x1 - ty, y1 + tx);
353 gui->graphics->draw_line (gc, x1 - ty, y1 + tx, x1 - tx, y1 - ty);
355 else if (x1 == x2 && y1 == y2)
357 gui->graphics->draw_arc (gc, x1, y1, t, t, 0, 360);
359 else
361 /* Slanted round-end pads. */
362 Coord dx, dy, ox, oy;
363 double h;
365 dx = x2 - x1;
366 dy = y2 - y1;
367 h = t / sqrt (SQUARE (dx) + SQUARE (dy));
368 ox = dy * h + 0.5 * SGN (dy);
369 oy = -(dx * h + 0.5 * SGN (dx));
371 gui->graphics->draw_line (gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
373 if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
375 Angle angle = atan2 (dx, dy) * 57.295779;
376 gui->graphics->draw_line (gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
377 gui->graphics->draw_arc (gc, x1, y1, t, t, angle - 180, 180);
378 gui->graphics->draw_arc (gc, x2, y2, t, t, angle, 180);
383 void
384 common_fill_pcb_pad (hidGC gc, PadType *pad, bool clear, bool mask)
386 Coord w = clear ? (mask ? pad->Mask
387 : pad->Thickness + pad->Clearance)
388 : pad->Thickness;
390 if (pad->Point1.X == pad->Point2.X &&
391 pad->Point1.Y == pad->Point2.Y)
393 if (TEST_FLAG (SQUAREFLAG, pad))
395 Coord l, r, t, b;
396 l = pad->Point1.X - w / 2;
397 b = pad->Point1.Y - w / 2;
398 r = l + w;
399 t = b + w;
400 gui->graphics->fill_rect (gc, l, b, r, t);
402 else
404 gui->graphics->fill_circle (gc, pad->Point1.X, pad->Point1.Y, w / 2);
407 else
409 gui->graphics->set_line_cap (gc, TEST_FLAG (SQUAREFLAG, pad) ?
410 Square_Cap : Round_Cap);
411 gui->graphics->set_line_width (gc, w);
413 gui->graphics->draw_line (gc, pad->Point1.X, pad->Point1.Y,
414 pad->Point2.X, pad->Point2.Y);
418 /* ---------------------------------------------------------------------------
419 * draws one polygon
420 * x and y are already in display coordinates
421 * the points are numbered:
423 * 5 --- 6
424 * / \
425 * 4 7
426 * | |
427 * 3 0
428 * \ /
429 * 2 --- 1
432 typedef struct
434 double X, Y;
436 FloatPolyType;
438 static void
439 draw_octagon_poly (hidGC gc, Coord X, Coord Y,
440 Coord Thickness, bool thin_draw)
442 static FloatPolyType p[8] = {
443 { 0.5, -TAN_22_5_DEGREE_2},
444 { TAN_22_5_DEGREE_2, -0.5 },
445 {-TAN_22_5_DEGREE_2, -0.5 },
446 {-0.5, -TAN_22_5_DEGREE_2},
447 {-0.5, TAN_22_5_DEGREE_2},
448 {-TAN_22_5_DEGREE_2, 0.5 },
449 { TAN_22_5_DEGREE_2, 0.5 },
450 { 0.5, TAN_22_5_DEGREE_2}
452 static int special_size = 0;
453 static int scaled_x[8];
454 static int scaled_y[8];
455 Coord polygon_x[9];
456 Coord polygon_y[9];
457 int i;
459 if (Thickness != special_size)
461 special_size = Thickness;
462 for (i = 0; i < 8; i++)
464 scaled_x[i] = p[i].X * special_size;
465 scaled_y[i] = p[i].Y * special_size;
468 /* add line offset */
469 for (i = 0; i < 8; i++)
471 polygon_x[i] = X + scaled_x[i];
472 polygon_y[i] = Y + scaled_y[i];
475 if (thin_draw)
477 int i;
478 gui->graphics->set_line_cap (gc, Round_Cap);
479 gui->graphics->set_line_width (gc, 0);
480 polygon_x[8] = X + scaled_x[0];
481 polygon_y[8] = Y + scaled_y[0];
482 for (i = 0; i < 8; i++)
483 gui->graphics->draw_line (gc, polygon_x[i ], polygon_y[i ],
484 polygon_x[i + 1], polygon_y[i + 1]);
486 else
487 gui->graphics->fill_polygon (gc, 8, polygon_x, polygon_y);
490 void
491 common_fill_pcb_pv (hidGC fg_gc, hidGC bg_gc, PinType *pv, bool drawHole, bool mask)
493 Coord w = mask ? pv->Mask : pv->Thickness;
494 Coord r = w / 2;
496 if (TEST_FLAG (HOLEFLAG, pv))
498 if (mask)
499 gui->graphics->fill_circle (bg_gc, pv->X, pv->Y, r);
500 if (drawHole)
502 gui->graphics->fill_circle (bg_gc, pv->X, pv->Y, r);
503 gui->graphics->set_line_cap (fg_gc, Round_Cap);
504 gui->graphics->set_line_width (fg_gc, 0);
505 gui->graphics->draw_arc (fg_gc, pv->X, pv->Y, r, r, 0, 360);
507 return;
510 if (TEST_FLAG (SQUAREFLAG, pv))
512 Coord l = pv->X - r;
513 Coord b = pv->Y - r;
514 Coord r = l + w;
515 Coord t = b + w;
517 gui->graphics->fill_rect (fg_gc, l, b, r, t);
519 else if (TEST_FLAG (OCTAGONFLAG, pv))
520 draw_octagon_poly (fg_gc, pv->X, pv->Y, w, false);
521 else /* draw a round pin or via */
522 gui->graphics->fill_circle (fg_gc, pv->X, pv->Y, r);
524 /* and the drilling hole (which is always round) */
525 if (drawHole)
526 gui->graphics->fill_circle (bg_gc, pv->X, pv->Y, pv->DrillingHole / 2);
529 void
530 common_thindraw_pcb_pv (hidGC fg_gc, hidGC bg_gc, PinType *pv, bool drawHole, bool mask)
532 Coord w = mask ? pv->Mask : pv->Thickness;
533 Coord r = w / 2;
535 if (TEST_FLAG (HOLEFLAG, pv))
537 if (mask)
538 gui->graphics->draw_arc (fg_gc, pv->X, pv->Y, r, r, 0, 360);
539 if (drawHole)
541 r = pv->DrillingHole / 2;
542 gui->graphics->set_line_cap (bg_gc, Round_Cap);
543 gui->graphics->set_line_width (bg_gc, 0);
544 gui->graphics->draw_arc (bg_gc, pv->X, pv->Y, r, r, 0, 360);
546 return;
549 if (TEST_FLAG (SQUAREFLAG, pv))
551 Coord l = pv->X - r;
552 Coord b = pv->Y - r;
553 Coord r = l + w;
554 Coord t = b + w;
556 gui->graphics->set_line_cap (fg_gc, Round_Cap);
557 gui->graphics->set_line_width (fg_gc, 0);
558 gui->graphics->draw_line (fg_gc, r, t, r, b);
559 gui->graphics->draw_line (fg_gc, l, t, l, b);
560 gui->graphics->draw_line (fg_gc, r, t, l, t);
561 gui->graphics->draw_line (fg_gc, r, b, l, b);
564 else if (TEST_FLAG (OCTAGONFLAG, pv))
566 draw_octagon_poly (fg_gc, pv->X, pv->Y, w, true);
568 else /* draw a round pin or via */
570 gui->graphics->set_line_cap (fg_gc, Round_Cap);
571 gui->graphics->set_line_width (fg_gc, 0);
572 gui->graphics->draw_arc (fg_gc, pv->X, pv->Y, r, r, 0, 360);
575 /* and the drilling hole (which is always round */
576 if (drawHole)
578 gui->graphics->set_line_cap (bg_gc, Round_Cap);
579 gui->graphics->set_line_width (bg_gc, 0);
580 gui->graphics->draw_arc (bg_gc, pv->X, pv->Y, pv->DrillingHole / 2,
581 pv->DrillingHole / 2, 0, 360);
585 void
586 common_draw_helpers_init (HID_DRAW *graphics)
588 graphics->draw_pcb_line = common_draw_pcb_line;
589 graphics->draw_pcb_arc = common_draw_pcb_arc;
590 graphics->draw_pcb_text = common_draw_pcb_text;
592 graphics->fill_pcb_polygon = common_fill_pcb_polygon;
593 graphics->thindraw_pcb_polygon = common_thindraw_pcb_polygon;
594 graphics->fill_pcb_pad = common_fill_pcb_pad;
595 graphics->thindraw_pcb_pad = common_thindraw_pcb_pad;
596 graphics->fill_pcb_pv = common_fill_pcb_pv;
597 graphics->thindraw_pcb_pv = common_thindraw_pcb_pv;