static some internal functions
[geda-gerbv/spe.git] / src / draw_amacro.c
blob99202b87b959bd00888837501769805dc54d1e0d
1 /*
2 * gEDA - GNU Electronic Design Automation
3 * This file is a part of gerbv.
5 * Copyright (C) 2000-2002 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 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <math.h> /* M_PI */
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #endif
36 #include <gtk/gtk.h>
38 #include "draw_amacro.h"
40 #undef round
41 #define round(x) floor((double)(x) + 0.5)
45 * Stack declarations and operations to be used by the simple engine that
46 * executes the parsed aperture macros.
48 typedef struct {
49 double *stack;
50 int sp;
51 } stack_t;
54 static stack_t *
55 new_stack(unsigned int nuf_push)
57 const int extra_stack_size = 10;
58 stack_t *s;
60 s = (stack_t *)malloc(sizeof(stack_t));
61 if (!s) {
62 free(s);
63 return NULL;
65 memset(s, 0, sizeof(stack_t));
67 s->stack = (double *)malloc(sizeof(double) * (nuf_push + extra_stack_size));
68 if (!s->stack) {
69 free(s->stack);
70 return NULL;
73 memset(s->stack, 0, sizeof(double) * (nuf_push + extra_stack_size));
74 s->sp = 0;
76 return s;
77 } /* new_stack */
80 static void
81 free_stack(stack_t *s)
83 if (s && s->stack)
84 free(s->stack);
86 if (s)
87 free(s);
89 return;
90 } /* free_stack */
93 static void
94 push(stack_t *s, double val)
96 s->stack[s->sp++] = val;
97 return;
98 } /* push */
101 static double
102 pop(stack_t *s)
104 return s->stack[--s->sp];
105 } /* pop */
109 * If you want to rotate a
110 * column vector v by t degrees using matrix M, use
112 * M = {{cos t, -sin t}, {sin t, cos t}} in M*v.
114 * From comp.graphics.algorithms Frequently Asked Questions
116 * Due reverse defintion of X-axis in GTK you have to negate
117 * angels.
120 static GdkPoint
121 rotate_point(GdkPoint point, int angle)
123 double sint, cost;
124 GdkPoint returned;
126 if (angle == 0)
127 return point;
129 sint = sin(-(double)angle * M_PI / 180.0);
130 cost = cos(-(double)angle * M_PI / 180.0);
132 returned.x = (int)round(cost * (double)point.x - sint * (double)point.y);
133 returned.y = (int)round(sint * (double)point.x + cost * (double)point.y);
135 return returned;
140 * Doesn't handle exposure yet and explicit x,y
142 static void
143 gerbv_draw_prim1(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
144 gint x, gint y)
146 const int diameter_idx = 1;
147 const gint full_circle = 23360;
148 GdkGC *local_gc = gdk_gc_new(pixmap);
149 gint dia = round(fabs(s->stack[diameter_idx] * scale));
150 gint real_x = x - dia / 2;
151 gint real_y = y - dia / 2;
153 gdk_gc_copy(local_gc, gc);
154 gdk_gc_set_line_attributes(local_gc,
155 1, /* outline always 1 pixels */
156 GDK_LINE_SOLID,
157 GDK_CAP_BUTT,
158 GDK_JOIN_MITER);
161 * A filled circle
163 gdk_draw_arc(pixmap, local_gc, 1, real_x, real_y, dia, dia,
164 0, full_circle);
166 gdk_gc_unref(local_gc);
168 return;
169 } /* gerbv_draw_prim1 */
173 * Doesn't handle exposure yet and explicit x,y
174 * Questions:
175 * - should start point be included in number of points?
176 * - how thick is the outline?
178 static void
179 gerbv_draw_prim4(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
180 gint x, gint y)
182 const int nuf_points_idx = 1;
183 const int first_x_idx = 2;
184 const int first_y_idx = 3;
185 const int rotext_idx = 4;
186 GdkGC *local_gc = gdk_gc_new(pixmap);
187 int nuf_points, point, closed_shape;
188 double rotation;
189 GdkPoint *points;
191 nuf_points = (int)s->stack[nuf_points_idx];
192 points = (GdkPoint *)malloc(sizeof(GdkPoint) * nuf_points);
193 if (!points) {
194 free(points);
195 return;
199 * Closed (ie filled as I interpret it) shape if first and last point
200 * are the same.
202 closed_shape =
203 (fabs(s->stack[first_x_idx] - s->stack[nuf_points * 2 + first_x_idx]) < 0.0001) &&
204 (fabs(s->stack[first_y_idx] - s->stack[nuf_points * 2 + first_y_idx]) < 0.0001);
206 rotation = s->stack[nuf_points * 2 + rotext_idx];
207 for (point = 0; point < nuf_points; point++) {
208 points[point].x = (int)round(scale * s->stack[point * 2 + first_x_idx]);
209 points[point].y = -(int)round(scale * s->stack[point * 2 + first_y_idx]);
210 if (rotation > 0.1)
211 points[point] = rotate_point(points[point], rotation);
212 points[point].x += x;
213 points[point].y += y;
216 gdk_gc_copy(local_gc, gc);
217 gdk_gc_set_line_attributes(local_gc,
218 1, /* outline always 1 pixels */
219 GDK_LINE_SOLID,
220 GDK_CAP_BUTT,
221 GDK_JOIN_MITER);
222 gdk_draw_polygon(pixmap, local_gc, closed_shape, points, nuf_points);
224 free(points);
225 gdk_gc_unref(local_gc);
226 return;
227 } /* gerbv_draw_prim4 */
231 * Doesn't handle exposure yet and explicit x,y
233 static void
234 gerbv_draw_prim5(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
235 gint x, gint y)
237 const int nuf_vertices_idx = 1;
238 const int diameter_idx = 4;
239 const int rotation_idx = 5;
240 int nuf_vertices, i;
241 double vertex, tick, rotation, radius;
242 GdkPoint *points;
244 if (s->sp != 6)
245 return;
247 nuf_vertices = (int)s->stack[nuf_vertices_idx];
248 points = (GdkPoint *)malloc(sizeof(GdkPoint) * nuf_vertices);
249 if (!points) {
250 free(points);
251 return;
254 tick = 2 * M_PI / (double)nuf_vertices;
255 rotation = -s->stack[rotation_idx] * M_PI / 180.0;
256 radius = s->stack[diameter_idx] / 2.0;
257 for (i = 0; i < nuf_vertices; i++) {
258 vertex = tick * (double)i + rotation;
259 points[i].x = (int)round(scale * radius * cos(vertex)) + x;
260 points[i].y = (int)round(scale * radius * sin(vertex)) + y;
263 gdk_draw_polygon(pixmap, gc, 1, points, nuf_vertices);
265 free(points);
266 return;
267 } /* gerbv_draw_prim5 */
271 * Doesn't handle exposure yet and explicit x,y
272 * Questions:
273 * - is "gap" distance between edges of circles or distance between
274 * center of line of circle?
276 static void
277 gerbv_draw_prim6(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
278 gint x, gint y)
280 const int outside_dia_idx = 2;
281 const int ci_thickness_idx = 3;
282 const int gap_idx = 4;
283 const int nuf_circles_idx = 5;
284 const int ch_thickness_idx = 6;
285 const int ch_length_idx = 7;
286 const int rotation_idx = 8;
287 GdkGC *local_gc = gdk_gc_new(pixmap);
288 double real_dia;
289 double real_gap;
290 int circle;
291 GdkPoint crosshair[4];
292 int point;
294 gdk_gc_copy(local_gc, gc);
295 gdk_gc_set_line_attributes(local_gc,
296 (int)round(scale * s->stack[ci_thickness_idx]),
297 GDK_LINE_SOLID,
298 GDK_CAP_BUTT,
299 GDK_JOIN_MITER);
301 real_dia = s->stack[outside_dia_idx] - s->stack[ci_thickness_idx] / 2.0;
302 real_gap = s->stack[gap_idx] + s->stack[ci_thickness_idx];
304 for (circle = 0; circle != (int)s->stack[nuf_circles_idx]; circle++) {
306 * Non filled circle
308 const gint full_circle = 23360;
309 gint dia = (real_dia - real_gap * circle) * scale;
310 gdk_draw_arc(pixmap, local_gc, 0, x - dia / 2, y - dia / 2,
311 dia, dia, 0, full_circle);
316 * Cross Hair
318 memset(crosshair, 0, sizeof(GdkPoint) * 4);
319 crosshair[0].x = (int)((s->stack[ch_length_idx] / 2.0) * scale);
320 /*crosshair[0].y = 0;*/
321 crosshair[1].x = -crosshair[0].x;
322 /*crosshair[1].y = 0;*/
323 /*crosshair[2].x = 0;*/
324 crosshair[2].y = crosshair[0].x;
325 /*crosshair[3].x = 0;*/
326 crosshair[3].y = -crosshair[0].x;
328 gdk_gc_set_line_attributes(local_gc,
329 (int)round(scale * s->stack[ch_thickness_idx]),
330 GDK_LINE_SOLID,
331 GDK_CAP_BUTT,
332 GDK_JOIN_MITER);
334 for (point = 0; point < 4; point++) {
335 crosshair[point] = rotate_point(crosshair[point],
336 s->stack[rotation_idx]);
337 crosshair[point].x += x;
338 crosshair[point].y += y;
340 gdk_draw_line(pixmap, local_gc,
341 crosshair[0].x, crosshair[0].y,
342 crosshair[1].x, crosshair[1].y);
343 gdk_draw_line(pixmap, local_gc,
344 crosshair[2].x, crosshair[2].y,
345 crosshair[3].x, crosshair[3].y);
347 gdk_gc_unref(local_gc);
349 return;
350 } /* gerbv_draw_prim6 */
353 static void
354 gerbv_draw_prim7(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
355 gint x, gint y)
357 const int outside_dia_idx = 2;
358 const int inside_dia_idx = 3;
359 const int ch_thickness_idx = 4;
360 const int rotation_idx = 5;
361 const gint full_circle = 23360;
362 GdkGCValues gc_val;
363 int diameter, i;
364 GdkGC *local_gc = gdk_gc_new(pixmap);
365 GdkPoint point[4];
366 double ci_thickness = (s->stack[outside_dia_idx] -
367 s->stack[inside_dia_idx]) / 2.0;
369 gdk_gc_copy(local_gc, gc);
370 gdk_gc_set_line_attributes(local_gc,
371 (int)round(scale * ci_thickness),
372 GDK_LINE_SOLID,
373 GDK_CAP_BUTT,
374 GDK_JOIN_MITER);
377 * Non filled circle
379 diameter = (s->stack[inside_dia_idx] + ci_thickness) * scale;
380 gdk_draw_arc(pixmap, local_gc, 0, x - diameter / 2, y - diameter / 2,
381 diameter, diameter, 0, full_circle);
384 * Cross hair
386 /* Calculate the end points of the crosshair */
387 for (i = 0; i < 4; i++) {
388 point[i].x = round((s->stack[outside_dia_idx] / 2.0) * scale);
389 point[i].y = 0;
390 point[i] = rotate_point(point[i], s->stack[rotation_idx] + 90 * i);
391 point[i].x += x;
392 point[i].y += y;
395 /* We must "reach out" to the outer part of the circle, hence round end */
396 gdk_gc_set_line_attributes(local_gc,
397 (int)round(scale * s->stack[ch_thickness_idx]),
398 GDK_LINE_SOLID,
399 GDK_CAP_ROUND,
400 GDK_JOIN_MITER);
402 /* The cross hair should "cut out" parts of the circle, hence inverse */
403 gdk_gc_get_values(local_gc, &gc_val);
404 if (gc_val.foreground.pixel == 1)
405 gc_val.foreground.pixel = 0;
406 else
407 gc_val.foreground.pixel = 1;
408 gdk_gc_set_foreground(local_gc, &(gc_val.foreground));
410 /* Draw the actual cross */
411 gdk_draw_line(pixmap, local_gc,
412 point[0].x, point[0].y, point[2].x, point[2].y);
413 gdk_draw_line(pixmap, local_gc,
414 point[1].x, point[1].y, point[3].x, point[3].y);
416 gdk_gc_unref(local_gc);
418 return;
419 } /* gerbv_draw_prim7 */
423 * Doesn't handle exposure yet and explicit x,y
425 static void
426 gerbv_draw_prim20(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
427 gint x, gint y)
429 const int linewidth_idx = 1;
430 const int start_x_idx = 2;
431 const int start_y_idx = 3;
432 const int end_x_idx = 4;
433 const int end_y_idx = 5;
434 const int rotation_idx = 6;
435 const int nuf_points = 2;
436 GdkGC *local_gc = gdk_gc_new(pixmap);
437 GdkPoint points[nuf_points];
438 int i;
440 gdk_gc_copy(local_gc, gc);
442 gdk_gc_set_line_attributes(local_gc,
443 (int)round(scale * s->stack[linewidth_idx]),
444 GDK_LINE_SOLID,
445 GDK_CAP_BUTT,
446 GDK_JOIN_MITER);
448 points[0].x = (s->stack[start_x_idx] * scale);
449 points[0].y = (s->stack[start_y_idx] * scale);
450 points[1].x = (s->stack[end_x_idx] * scale);
451 points[1].y = (s->stack[end_y_idx] * scale);
453 for (i = 0; i < nuf_points; i++) {
454 points[i] = rotate_point(points[i], s->stack[rotation_idx]);
455 points[i].x = x + points[i].x;
456 points[i].y = y - points[i].y;
459 gdk_draw_line(pixmap, local_gc,
460 points[0].x, points[0].y,
461 points[1].x, points[1].y);
463 gdk_gc_unref(local_gc);
465 return;
466 } /* gerbv_draw_prim20 */
470 * Doesn't handle exposure yet and explicit x,y
472 static void
473 gerbv_draw_prim21(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
474 gint x, gint y)
476 const int width_idx = 1;
477 const int height_idx = 2;
478 const int rotation_idx = 5;
479 const int nuf_points = 4;
480 GdkPoint points[nuf_points];
481 int half_width, half_height;
482 int i;
484 half_width = (int)round(s->stack[width_idx] * scale / 2.0);
485 half_height =(int)round(s->stack[height_idx] * scale / 2.0);
487 points[0].x = half_width;
488 points[0].y = half_height;
490 points[1].x = half_width;
491 points[1].y = -half_height;
493 points[2].x = -half_width;
494 points[2].y = -half_height;
496 points[3].x = -half_width;
497 points[3].y = half_height;
499 for (i = 0; i < nuf_points; i++) {
500 points[i] = rotate_point(points[i], s->stack[rotation_idx]);
501 points[i].x += x;
502 points[i].y += y;
505 gdk_draw_polygon(pixmap, gc, 1, points, nuf_points);
507 return;
508 } /* gerbv_draw_prim21 */
512 * Doesn't handle exposure yet and explicit x,y
514 static void
515 gerbv_draw_prim22(GdkPixmap *pixmap, GdkGC *gc, stack_t *s, int scale,
516 gint x, gint y)
518 const int width_idx = 1;
519 const int height_idx = 2;
520 const int x_lower_left_idx = 3;
521 const int y_lower_left_idx = 4;
522 const int rotation_idx = 5;
523 const int nuf_points = 4;
524 GdkPoint points[nuf_points];
525 int i;
527 points[0].x = (int)round(s->stack[x_lower_left_idx] * scale);
528 points[0].y = (int)round(s->stack[y_lower_left_idx] * scale);
530 points[1].x = (int)round((s->stack[x_lower_left_idx] + s->stack[width_idx])
531 * scale);
532 points[1].y = (int)round(s->stack[y_lower_left_idx] * scale);
534 points[2].x = (int)round((s->stack[x_lower_left_idx] + s->stack[width_idx])
535 * scale);
536 points[2].y = (int)round((s->stack[y_lower_left_idx] - s->stack[height_idx])
537 * scale);
539 points[3].x = (int)round(s->stack[x_lower_left_idx] * scale);
540 points[3].y = (int)round((s->stack[y_lower_left_idx] - s->stack[height_idx])
541 * scale);
543 for (i = 0; i < nuf_points; i++) {
544 points[i] = rotate_point(points[i], s->stack[rotation_idx]);
545 points[i].x += x;
546 points[i].y += y;
549 gdk_draw_polygon(pixmap, gc, 1, points, nuf_points);
551 return;
552 } /* gerbv_draw_prim22 */
556 gerbv_draw_amacro(GdkPixmap *pixmap, GdkGC *gc,
557 instruction_t *program, unsigned int nuf_push,
558 double *parameters, int scale, gint x, gint y)
560 stack_t *s = new_stack(nuf_push);
561 instruction_t *ip;
562 int handled = 1;
564 for(ip = program; ip != NULL; ip = ip->next) {
565 switch(ip->opcode) {
566 case NOP:
567 break;
568 case PUSH :
569 push(s, ip->data.fval);
570 break;
571 case PPUSH :
572 push(s, parameters[ip->data.ival - 1]);
573 break;
574 case ADD :
575 push(s, pop(s) + pop(s));
576 break;
577 case SUB :
578 push(s, -pop(s) + pop(s));
579 break;
580 case MUL :
581 push(s, pop(s) * pop(s));
582 break;
583 case DIV :
584 push(s, 1 / ((pop(s) / pop(s))));
585 break;
586 case PRIM :
587 switch(ip->data.ival) {
588 case 1:
589 gerbv_draw_prim1(pixmap, gc, s, scale, x, y);
590 break;
591 case 4 :
592 gerbv_draw_prim4(pixmap, gc, s, scale, x, y);
593 break;
594 case 5 :
595 gerbv_draw_prim5(pixmap, gc, s, scale, x, y);
596 break;
597 case 6 :
598 gerbv_draw_prim6(pixmap, gc, s, scale, x, y);
599 break;
600 case 7 :
601 gerbv_draw_prim7(pixmap, gc, s, scale, x, y);
602 break;
603 case 2 :
604 case 20 :
605 gerbv_draw_prim20(pixmap, gc, s, scale, x, y);
606 break;
607 case 21 :
608 gerbv_draw_prim21(pixmap, gc, s, scale, x, y);
609 break;
610 case 22 :
611 gerbv_draw_prim22(pixmap, gc, s, scale, x, y);
612 break;
613 default :
614 handled = 0;
617 * Here we reset the stack pointer. It's not general correct
618 * correct to do this, but since I know how the compiler works
619 * I can do this. The correct way to do this should be to
620 * subtract number of used elements in each primitive operation.
622 s->sp = 0;
623 break;
624 default :
625 break;
628 free_stack(s);
629 return handled;
630 } /* gerbv_draw_amacro */