implemented grid thickness
[geda-pcb.git] / src / hid / gtk / gtkhid-gl.c
blob4afd99e65bbea351c92e268e1c2ccaf3d54de6dd
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <stdio.h>
7 #include "crosshair.h"
8 #include "clip.h"
9 #include "../hidint.h"
10 #include "gui.h"
11 #include "gui-pinout-preview.h"
13 /* The Linux OpenGL ABI 1.0 spec requires that we define
14 * GL_GLEXT_PROTOTYPES before including gl.h or glx.h for extensions
15 * in order to get prototypes:
16 * http://www.opengl.org/registry/ABI/
18 #define GL_GLEXT_PROTOTYPES 1
20 /* This follows autoconf's recommendation for the AX_CHECK_GL macro
21 https://www.gnu.org/software/autoconf-archive/ax_check_gl.html */
22 #if defined HAVE_WINDOWS_H && defined _WIN32
23 # include <windows.h>
24 #endif
25 #if defined HAVE_GL_GL_H
26 # include <GL/gl.h>
27 #elif defined HAVE_OPENGL_GL_H
28 # include <OpenGL/gl.h>
29 #else
30 # error autoconf couldnt find gl.h
31 #endif
33 #include <gtk/gtkgl.h>
34 #include "hid/common/hidgl.h"
36 #include "hid/common/draw_helpers.h"
37 #include "hid/common/trackball.h"
39 #ifdef HAVE_LIBDMALLOC
40 #include <dmalloc.h>
41 #endif
43 extern HID ghid_hid;
44 extern HID_DRAW ghid_graphics;
46 static hidGC current_gc = NULL;
48 /* Sets gport->u_gc to the "right" GC to use (wrt mask or window)
50 #define USE_GC(gc) if (!use_gc(gc)) return
52 static enum mask_mode cur_mask = HID_MASK_OFF;
53 static GLfloat view_matrix[4][4] = {{1.0, 0.0, 0.0, 0.0},
54 {0.0, 1.0, 0.0, 0.0},
55 {0.0, 0.0, 1.0, 0.0},
56 {0.0, 0.0, 0.0, 1.0}};
57 static GLfloat last_modelview_matrix[4][4] = {{1.0, 0.0, 0.0, 0.0},
58 {0.0, 1.0, 0.0, 0.0},
59 {0.0, 0.0, 1.0, 0.0},
60 {0.0, 0.0, 0.0, 1.0}};
61 static int global_view_2d = 1;
63 typedef struct render_priv {
64 GdkGLConfig *glconfig;
65 bool trans_lines;
66 bool in_context;
67 int subcomposite_stencil_bit;
68 char *current_colorname;
69 double current_alpha_mult;
70 GTimer *time_since_expose;
72 /* Feature for leading the user to a particular location */
73 guint lead_user_timeout;
74 GTimer *lead_user_timer;
75 bool lead_user;
76 Coord lead_user_radius;
77 Coord lead_user_x;
78 Coord lead_user_y;
80 hidGC crosshair_gc;
81 } render_priv;
84 typedef struct hid_gc_struct
86 HID *me_pointer;
88 const char *colorname;
89 double alpha_mult;
90 Coord width;
91 gint cap, join;
93 hid_gc_struct;
96 static void draw_lead_user (render_priv *priv);
97 static void ghid_unproject_to_z_plane (int ex, int ey, Coord pcb_z, Coord *pcb_x, Coord *pcb_y);
100 static void
101 start_subcomposite (void)
103 render_priv *priv = gport->render_priv;
104 int stencil_bit;
106 /* Flush out any existing geoemtry to be rendered */
107 hidgl_flush_triangles (&buffer);
109 glEnable (GL_STENCIL_TEST); /* Enable Stencil test */
110 glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); /* Stencil pass => replace stencil value (with 1) */
112 stencil_bit = hidgl_assign_clear_stencil_bit(); /* Get a new (clean) bitplane to stencil with */
113 glStencilMask (stencil_bit); /* Only write to our subcompositing stencil bitplane */
114 glStencilFunc (GL_GREATER, stencil_bit, stencil_bit); /* Pass stencil test if our assigned bit is clear */
116 priv->subcomposite_stencil_bit = stencil_bit;
119 static void
120 end_subcomposite (void)
122 render_priv *priv = gport->render_priv;
124 /* Flush out any existing geoemtry to be rendered */
125 hidgl_flush_triangles (&buffer);
127 hidgl_return_stencil_bit (priv->subcomposite_stencil_bit); /* Relinquish any bitplane we previously used */
129 glStencilMask (0);
130 glStencilFunc (GL_ALWAYS, 0, 0); /* Always pass stencil test */
131 glDisable (GL_STENCIL_TEST); /* Disable Stencil test */
133 priv->subcomposite_stencil_bit = 0;
138 ghid_set_layer (const char *name, int group, int empty)
140 render_priv *priv = gport->render_priv;
141 int idx = group;
142 if (idx >= 0 && idx < max_group)
144 int n = PCB->LayerGroups.Number[group];
145 for (idx = 0; idx < n-1; idx ++)
147 int ni = PCB->LayerGroups.Entries[group][idx];
148 if (ni >= 0 && ni < max_copper_layer + SILK_LAYER
149 && PCB->Data->Layer[ni].On)
150 break;
152 idx = PCB->LayerGroups.Entries[group][idx];
155 end_subcomposite ();
156 start_subcomposite ();
158 if (idx >= 0 && idx < max_copper_layer + SILK_LAYER)
160 priv->trans_lines = true;
161 return PCB->Data->Layer[idx].On;
163 if (idx < 0)
165 switch (SL_TYPE (idx))
167 case SL_INVISIBLE:
168 return PCB->InvisibleObjectsOn;
169 case SL_MASK:
170 if (SL_MYSIDE (idx))
171 return TEST_FLAG (SHOWMASKFLAG, PCB);
172 return 0;
173 case SL_SILK:
174 priv->trans_lines = true;
175 if (SL_MYSIDE (idx))
176 return PCB->ElementOn;
177 return 0;
178 case SL_ASSY:
179 return 0;
180 case SL_PDRILL:
181 case SL_UDRILL:
182 return 1;
183 case SL_RATS:
184 if (PCB->RatOn)
185 priv->trans_lines = true;
186 return PCB->RatOn;
189 return 0;
192 static void
193 ghid_end_layer (void)
195 end_subcomposite ();
198 void
199 ghid_destroy_gc (hidGC gc)
201 g_free (gc);
204 hidGC
205 ghid_make_gc (void)
207 hidGC rv;
209 rv = g_new0 (hid_gc_struct, 1);
210 rv->me_pointer = &ghid_hid;
211 rv->colorname = Settings.BackgroundColor;
212 rv->alpha_mult = 1.0;
213 return rv;
216 void
217 ghid_draw_grid (BoxType *drawn_area)
219 if (Vz (PCB->Grid) < MIN_GRID_DISTANCE)
220 return;
222 if (gdk_color_parse (Settings.GridColor, &gport->grid_color))
224 gport->grid_color.red ^= gport->bg_color.red;
225 gport->grid_color.green ^= gport->bg_color.green;
226 gport->grid_color.blue ^= gport->bg_color.blue;
229 glEnable (GL_COLOR_LOGIC_OP);
230 glLogicOp (GL_XOR);
232 glColor3f (gport->grid_color.red / 65535.,
233 gport->grid_color.green / 65535.,
234 gport->grid_color.blue / 65535.);
236 hidgl_draw_grid (drawn_area, gport->view.coord_per_px);
238 glDisable (GL_COLOR_LOGIC_OP);
241 static void
242 ghid_draw_bg_image (void)
244 static GLuint texture_handle = 0;
246 if (!ghidgui->bg_pixbuf)
247 return;
249 if (texture_handle == 0)
251 int width = gdk_pixbuf_get_width (ghidgui->bg_pixbuf);
252 int height = gdk_pixbuf_get_height (ghidgui->bg_pixbuf);
253 int rowstride = gdk_pixbuf_get_rowstride (ghidgui->bg_pixbuf);
254 int bits_per_sample = gdk_pixbuf_get_bits_per_sample (ghidgui->bg_pixbuf);
255 int n_channels = gdk_pixbuf_get_n_channels (ghidgui->bg_pixbuf);
256 unsigned char *pixels = gdk_pixbuf_get_pixels (ghidgui->bg_pixbuf);
258 g_warn_if_fail (bits_per_sample == 8);
259 g_warn_if_fail (rowstride == width * n_channels);
261 glGenTextures (1, &texture_handle);
262 glBindTexture (GL_TEXTURE_2D, texture_handle);
264 /* XXX: We should proabbly determine what the maxmimum texture supported is,
265 * and if our image is larger, shrink it down using GDK pixbuf routines
266 * rather than having it fail below.
269 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
270 (n_channels == 4) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, pixels);
273 glBindTexture (GL_TEXTURE_2D, texture_handle);
275 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
276 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
277 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
278 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
279 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
280 glEnable (GL_TEXTURE_2D);
282 /* Render a quad with the background as a texture */
284 glBegin (GL_QUADS);
285 glTexCoord2d (0., 0.);
286 glVertex3i (0, 0, 0);
287 glTexCoord2d (1., 0.);
288 glVertex3i (PCB->MaxWidth, 0, 0);
289 glTexCoord2d (1., 1.);
290 glVertex3i (PCB->MaxWidth, PCB->MaxHeight, 0);
291 glTexCoord2d (0., 1.);
292 glVertex3i (0, PCB->MaxHeight, 0);
293 glEnd ();
295 glDisable (GL_TEXTURE_2D);
298 void
299 ghid_use_mask (enum mask_mode mode)
301 static int stencil_bit = 0;
303 if (mode == cur_mask)
304 return;
306 /* Flush out any existing geoemtry to be rendered */
307 hidgl_flush_triangles (&buffer);
309 switch (mode)
311 case HID_MASK_BEFORE:
312 /* The HID asks not to receive this mask type, so warn if we get it */
313 g_return_if_reached ();
315 case HID_MASK_CLEAR:
316 /* Write '1' to the stencil buffer where the solder-mask should not be drawn. */
317 glColorMask (0, 0, 0, 0); /* Disable writting in color buffer */
318 glEnable (GL_STENCIL_TEST); /* Enable Stencil test */
319 stencil_bit = hidgl_assign_clear_stencil_bit(); /* Get a new (clean) bitplane to stencil with */
320 glStencilFunc (GL_ALWAYS, stencil_bit, stencil_bit); /* Always pass stencil test, write stencil_bit */
321 glStencilMask (stencil_bit); /* Only write to our subcompositing stencil bitplane */
322 glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); /* Stencil pass => replace stencil value (with 1) */
323 break;
325 case HID_MASK_AFTER:
326 /* Drawing operations as masked to areas where the stencil buffer is '0' */
327 glColorMask (1, 1, 1, 1); /* Enable drawing of r, g, b & a */
328 glStencilFunc (GL_GEQUAL, 0, stencil_bit); /* Draw only where our bit of the stencil buffer is clear */
329 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); /* Stencil buffer read only */
330 break;
332 case HID_MASK_OFF:
333 /* Disable stenciling */
334 hidgl_return_stencil_bit (stencil_bit); /* Relinquish any bitplane we previously used */
335 glDisable (GL_STENCIL_TEST); /* Disable Stencil test */
336 break;
338 cur_mask = mode;
342 /* Config helper functions for when the user changes color preferences.
343 | set_special colors used in the gtkhid.
345 static void
346 set_special_grid_color (void)
348 if (!gport->colormap)
349 return;
350 gport->grid_color.red ^= gport->bg_color.red;
351 gport->grid_color.green ^= gport->bg_color.green;
352 gport->grid_color.blue ^= gport->bg_color.blue;
355 void
356 ghid_set_special_colors (HID_Attribute * ha)
358 if (!ha->name || !ha->value)
359 return;
360 if (!strcmp (ha->name, "background-color"))
362 ghid_map_color_string (*(char **) ha->value, &gport->bg_color);
363 set_special_grid_color ();
365 else if (!strcmp (ha->name, "off-limit-color"))
367 ghid_map_color_string (*(char **) ha->value, &gport->offlimits_color);
369 else if (!strcmp (ha->name, "grid-color"))
371 ghid_map_color_string (*(char **) ha->value, &gport->grid_color);
372 set_special_grid_color ();
376 typedef struct
378 int color_set;
379 GdkColor color;
380 double red;
381 double green;
382 double blue;
383 } ColorCache;
385 static void
386 set_gl_color_for_gc (hidGC gc)
388 render_priv *priv = gport->render_priv;
389 static void *cache = NULL;
390 hidval cval;
391 ColorCache *cc;
392 double r, g, b, a;
394 if (priv->current_colorname != NULL &&
395 strcmp (priv->current_colorname, gc->colorname) == 0 &&
396 priv->current_alpha_mult == gc->alpha_mult)
397 return;
399 free (priv->current_colorname);
400 priv->current_colorname = strdup (gc->colorname);
401 priv->current_alpha_mult = gc->alpha_mult;
403 if (gport->colormap == NULL)
404 gport->colormap = gtk_widget_get_colormap (gport->top_window);
405 if (strcmp (gc->colorname, "erase") == 0)
407 r = gport->bg_color.red / 65535.;
408 g = gport->bg_color.green / 65535.;
409 b = gport->bg_color.blue / 65535.;
410 a = 1.0;
412 else if (strcmp (gc->colorname, "drill") == 0)
414 r = gport->offlimits_color.red / 65535.;
415 g = gport->offlimits_color.green / 65535.;
416 b = gport->offlimits_color.blue / 65535.;
417 a = 0.85;
419 else
421 if (hid_cache_color (0, gc->colorname, &cval, &cache))
422 cc = (ColorCache *) cval.ptr;
423 else
425 cc = (ColorCache *) malloc (sizeof (ColorCache));
426 memset (cc, 0, sizeof (*cc));
427 cval.ptr = cc;
428 hid_cache_color (1, gc->colorname, &cval, &cache);
431 if (!cc->color_set)
433 if (gdk_color_parse (gc->colorname, &cc->color))
434 gdk_color_alloc (gport->colormap, &cc->color);
435 else
436 gdk_color_white (gport->colormap, &cc->color);
437 cc->red = cc->color.red / 65535.;
438 cc->green = cc->color.green / 65535.;
439 cc->blue = cc->color.blue / 65535.;
440 cc->color_set = 1;
442 r = cc->red;
443 g = cc->green;
444 b = cc->blue;
445 a = 0.7;
447 if (1) {
448 double maxi, mult;
449 a *= gc->alpha_mult;
450 if (!priv->trans_lines)
451 a = 1.0;
452 maxi = r;
453 if (g > maxi) maxi = g;
454 if (b > maxi) maxi = b;
455 mult = MIN (1 / a, 1 / maxi);
456 #if 1
457 r = r * mult;
458 g = g * mult;
459 b = b * mult;
460 #endif
463 if(!priv->in_context)
464 return;
466 hidgl_flush_triangles (&buffer);
467 glColor4d (r, g, b, a);
470 void
471 ghid_set_color (hidGC gc, const char *name)
473 gc->colorname = name;
474 set_gl_color_for_gc (gc);
477 void
478 ghid_set_alpha_mult (hidGC gc, double alpha_mult)
480 gc->alpha_mult = alpha_mult;
481 set_gl_color_for_gc (gc);
484 void
485 ghid_set_line_cap (hidGC gc, EndCapStyle style)
487 gc->cap = style;
490 void
491 ghid_set_line_width (hidGC gc, Coord width)
493 gc->width = width;
497 void
498 ghid_set_draw_xor (hidGC gc, int xor)
500 /* NOT IMPLEMENTED */
502 /* Only presently called when setting up a crosshair GC.
503 * We manage our own drawing model for that anyway. */
506 void
507 ghid_set_draw_faded (hidGC gc, int faded)
509 printf ("ghid_set_draw_faded(%p,%d) -- not implemented\n", gc, faded);
512 void
513 ghid_set_line_cap_angle (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
515 printf ("ghid_set_line_cap_angle() -- not implemented\n");
518 static void
519 ghid_invalidate_current_gc (void)
521 current_gc = NULL;
524 static int
525 use_gc (hidGC gc)
527 if (gc->me_pointer != &ghid_hid)
529 fprintf (stderr, "Fatal: GC from another HID passed to GTK HID\n");
530 abort ();
533 if (current_gc == gc)
534 return 1;
536 current_gc = gc;
538 set_gl_color_for_gc (gc);
539 return 1;
542 void
543 ghid_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
545 USE_GC (gc);
547 hidgl_draw_line (gc->cap, gc->width, x1, y1, x2, y2, gport->view.coord_per_px);
550 void
551 ghid_draw_arc (hidGC gc, Coord cx, Coord cy, Coord xradius, Coord yradius,
552 Angle start_angle, Angle delta_angle)
554 USE_GC (gc);
556 hidgl_draw_arc (gc->width, cx, cy, xradius, yradius,
557 start_angle, delta_angle, gport->view.coord_per_px);
560 void
561 ghid_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
563 USE_GC (gc);
565 hidgl_draw_rect (x1, y1, x2, y2);
569 void
570 ghid_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
572 USE_GC (gc);
574 hidgl_fill_circle (cx, cy, radius, gport->view.coord_per_px);
578 void
579 ghid_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
581 USE_GC (gc);
583 hidgl_fill_polygon (n_coords, x, y);
586 void
587 ghid_fill_pcb_polygon (hidGC gc, PolygonType *poly, const BoxType *clip_box)
589 USE_GC (gc);
590 if (TEST_FLAG (THINDRAWFLAG, PCB) || TEST_FLAG (THINDRAWPOLYFLAG, PCB))
591 ghid_set_alpha_mult (gc, 0.25);
593 hidgl_fill_pcb_polygon (poly, clip_box, gport->view.coord_per_px);
594 ghid_set_alpha_mult (gc, 1.0);
598 * TODO: remove at next major revision
599 * This funciton was initially used to override the common function so that the
600 * hidgl HID would fill the thin-drawn polygons using transparency. Due to the
601 * implementation of opengl, all opaque objects must be drawn before transparent
602 * ones. As a result, in order to correctly draw objects in thin-draw mode, the
603 * drawing of objects and polygon outlines must be done first, prior to drawing
604 * the fill.
606 * The call to the common thindraw_pcb_polygon has been restored, and a second
607 * polygon draw phase has been added. The first phase only draws the outlines of
608 * the polygons and the second phase now draws the fill.
610 void
611 ghid_thindraw_pcb_polygon (hidGC gc, PolygonType *poly, const BoxType *clip_box)
613 common_thindraw_pcb_polygon (gc, poly, clip_box);
614 ghid_set_alpha_mult (gc, 0.25);
615 gui->graphics->fill_pcb_polygon (gc, poly, clip_box);
616 ghid_set_alpha_mult (gc, 1.0);
619 void
620 ghid_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
622 USE_GC (gc);
624 hidgl_fill_rect (x1, y1, x2, y2);
627 void
628 ghid_invalidate_lr (Coord left, Coord right, Coord top, Coord bottom)
630 ghid_invalidate_all ();
633 #define MAX_ELAPSED (50. / 1000.) /* 50ms */
634 void
635 ghid_invalidate_all ()
637 render_priv *priv = gport->render_priv;
638 double elapsed = g_timer_elapsed (priv->time_since_expose, NULL);
640 ghid_draw_area_update (gport, NULL);
642 if (elapsed > MAX_ELAPSED)
643 gdk_window_process_all_updates ();
646 void
647 ghid_notify_crosshair_change (bool changes_complete)
649 /* We sometimes get called before the GUI is up */
650 if (gport->drawing_area == NULL)
651 return;
653 /* FIXME: We could just invalidate the bounds of the crosshair attached objects? */
654 if (changes_complete) ghid_invalidate_all ();
657 void
658 ghid_notify_mark_change (bool changes_complete)
660 /* We sometimes get called before the GUI is up */
661 if (gport->drawing_area == NULL)
662 return;
664 /* FIXME: We could just invalidate the bounds of the mark? */
665 if (changes_complete) ghid_invalidate_all ();
668 static void
669 draw_right_cross (gint x, gint y, gint z)
671 glVertex3i (x, 0, z);
672 glVertex3i (x, PCB->MaxHeight, z);
673 glVertex3i (0, y, z);
674 glVertex3i (PCB->MaxWidth, y, z);
677 static void
678 draw_slanted_cross (gint x, gint y, gint z)
680 gint x0, y0, x1, y1;
682 x0 = x + (PCB->MaxHeight - y);
683 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
684 x1 = x - y;
685 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
686 y0 = y + (PCB->MaxWidth - x);
687 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
688 y1 = y - x;
689 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
690 glVertex3i (x0, y0, z);
691 glVertex3i (x1, y1, z);
693 x0 = x - (PCB->MaxHeight - y);
694 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
695 x1 = x + y;
696 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
697 y0 = y + x;
698 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
699 y1 = y - (PCB->MaxWidth - x);
700 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
701 glVertex3i (x0, y0, z);
702 glVertex3i (x1, y1, z);
705 static void
706 draw_dozen_cross (gint x, gint y, gint z)
708 gint x0, y0, x1, y1;
709 gdouble tan60 = sqrt (3);
711 x0 = x + (PCB->MaxHeight - y) / tan60;
712 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
713 x1 = x - y / tan60;
714 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
715 y0 = y + (PCB->MaxWidth - x) * tan60;
716 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
717 y1 = y - x * tan60;
718 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
719 glVertex3i (x0, y0, z);
720 glVertex3i (x1, y1, z);
722 x0 = x + (PCB->MaxHeight - y) * tan60;
723 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
724 x1 = x - y * tan60;
725 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
726 y0 = y + (PCB->MaxWidth - x) / tan60;
727 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
728 y1 = y - x / tan60;
729 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
730 glVertex3i (x0, y0, z);
731 glVertex3i (x1, y1, z);
733 x0 = x - (PCB->MaxHeight - y) / tan60;
734 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
735 x1 = x + y / tan60;
736 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
737 y0 = y + x * tan60;
738 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
739 y1 = y - (PCB->MaxWidth - x) * tan60;
740 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
741 glVertex3i (x0, y0, z);
742 glVertex3i (x1, y1, z);
744 x0 = x - (PCB->MaxHeight - y) * tan60;
745 x0 = MAX(0, MIN (x0, PCB->MaxWidth));
746 x1 = x + y * tan60;
747 x1 = MAX(0, MIN (x1, PCB->MaxWidth));
748 y0 = y + x / tan60;
749 y0 = MAX(0, MIN (y0, PCB->MaxHeight));
750 y1 = y - (PCB->MaxWidth - x) / tan60;
751 y1 = MAX(0, MIN (y1, PCB->MaxHeight));
752 glVertex3i (x0, y0, z);
753 glVertex3i (x1, y1, z);
756 static void
757 draw_crosshair (render_priv *priv)
759 gint x, y, z;
760 static int done_once = 0;
761 static GdkColor cross_color;
763 if (!done_once)
765 done_once = 1;
766 /* FIXME: when CrossColor changed from config */
767 ghid_map_color_string (Settings.CrossColor, &cross_color);
770 x = gport->crosshair_x;
771 y = gport->crosshair_y;
772 z = 0;
774 glEnable (GL_COLOR_LOGIC_OP);
775 glLogicOp (GL_XOR);
777 glColor3f (cross_color.red / 65535.,
778 cross_color.green / 65535.,
779 cross_color.blue / 65535.);
781 glBegin (GL_LINES);
783 draw_right_cross (x, y, z);
784 if (Crosshair.shape == Union_Jack_Crosshair_Shape)
785 draw_slanted_cross (x, y, z);
786 if (Crosshair.shape == Dozen_Crosshair_Shape)
787 draw_dozen_cross (x, y, z);
789 glEnd ();
791 glDisable (GL_COLOR_LOGIC_OP);
794 void
795 ghid_init_renderer (int *argc, char ***argv, GHidPort *port)
797 render_priv *priv;
799 port->render_priv = priv = g_new0 (render_priv, 1);
800 port->render_priv->crosshair_gc = gui->graphics->make_gc ();
802 priv->time_since_expose = g_timer_new ();
804 gtk_gl_init(argc, argv);
806 /* setup GL-context */
807 priv->glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGBA |
808 GDK_GL_MODE_STENCIL |
809 GDK_GL_MODE_DOUBLE);
810 if (!priv->glconfig)
812 printf ("Could not setup GL-context!\n");
813 return; /* Should we abort? */
816 /* Setup HID function pointers specific to the GL renderer*/
817 ghid_hid.end_layer = ghid_end_layer;
818 ghid_graphics.fill_pcb_polygon = ghid_fill_pcb_polygon;
819 /*ghid_graphics.thindraw_pcb_polygon = ghid_thindraw_pcb_polygon;*/
822 void
823 ghid_shutdown_renderer (GHidPort *port)
825 render_priv *priv = port->render_priv;
827 gui->graphics->destroy_gc (priv->crosshair_gc);
828 ghid_cancel_lead_user ();
829 g_free (port->render_priv);
830 port->render_priv = NULL;
833 void
834 ghid_init_drawing_widget (GtkWidget *widget, GHidPort *port)
836 render_priv *priv = port->render_priv;
838 gtk_widget_set_gl_capability (widget,
839 priv->glconfig,
840 NULL,
841 TRUE,
842 GDK_GL_RGBA_TYPE);
845 void
846 ghid_drawing_area_configure_hook (GHidPort *port)
850 gboolean
851 ghid_start_drawing (GHidPort *port, GtkWidget *widget)
853 GdkGLContext *pGlContext = gtk_widget_get_gl_context (widget);
854 GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable (widget);
856 /* make GL-context "current" */
857 if (!gdk_gl_drawable_gl_begin (pGlDrawable, pGlContext))
858 return FALSE;
860 port->render_priv->in_context = true;
862 return TRUE;
865 void
866 ghid_end_drawing (GHidPort *port, GtkWidget *widget)
868 GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable (widget);
870 if (gdk_gl_drawable_is_double_buffered (pGlDrawable))
871 gdk_gl_drawable_swap_buffers (pGlDrawable);
872 else
873 glFlush ();
875 port->render_priv->in_context = false;
877 /* end drawing to current GL-context */
878 gdk_gl_drawable_gl_end (pGlDrawable);
881 void
882 ghid_screen_update (void)
886 #define Z_NEAR 3.0
887 gboolean
888 ghid_drawing_area_expose_cb (GtkWidget *widget,
889 GdkEventExpose *ev,
890 GHidPort *port)
892 render_priv *priv = port->render_priv;
893 GtkAllocation allocation;
894 BoxType region;
895 Coord min_x, min_y;
896 Coord max_x, max_y;
897 Coord new_x, new_y;
898 Coord min_depth;
899 Coord max_depth;
901 gtk_widget_get_allocation (widget, &allocation);
903 ghid_start_drawing (port, widget);
904 hidgl_start_render ();
906 /* If we don't have any stencil bits available,
907 we can't use the hidgl polygon drawing routine */
908 /* TODO: We could use the GLU tessellator though */
909 if (hidgl_stencil_bits() == 0)
910 ghid_graphics.fill_pcb_polygon = common_fill_pcb_polygon;
912 glEnable (GL_BLEND);
913 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
915 glViewport (0, 0, allocation.width, allocation.height);
917 glEnable (GL_SCISSOR_TEST);
918 glScissor (ev->area.x,
919 allocation.height - ev->area.height - ev->area.y,
920 ev->area.width, ev->area.height);
922 glMatrixMode (GL_PROJECTION);
923 glLoadIdentity ();
924 glOrtho (0, allocation.width, allocation.height, 0, -100000, 100000);
925 glMatrixMode (GL_MODELVIEW);
926 glLoadIdentity ();
927 glTranslatef (widget->allocation.width / 2., widget->allocation.height / 2., 0);
928 glMultMatrixf ((GLfloat *)view_matrix);
929 glTranslatef (-widget->allocation.width / 2., -widget->allocation.height / 2., 0);
930 glScalef ((port->view.flip_x ? -1. : 1.) / port->view.coord_per_px,
931 (port->view.flip_y ? -1. : 1.) / port->view.coord_per_px,
932 ((port->view.flip_x == port->view.flip_y) ? 1. : -1.) / port->view.coord_per_px);
933 glTranslatef (port->view.flip_x ? port->view.x0 - PCB->MaxWidth :
934 -port->view.x0,
935 port->view.flip_y ? port->view.y0 - PCB->MaxHeight :
936 -port->view.y0, 0);
937 glGetFloatv (GL_MODELVIEW_MATRIX, (GLfloat *)last_modelview_matrix);
939 glEnable (GL_STENCIL_TEST);
940 glClearColor (port->offlimits_color.red / 65535.,
941 port->offlimits_color.green / 65535.,
942 port->offlimits_color.blue / 65535.,
943 1.);
944 glStencilMask (~0);
945 glClearStencil (0);
946 glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
947 hidgl_reset_stencil_usage ();
949 /* Disable the stencil test until we need it - otherwise it gets dirty */
950 glDisable (GL_STENCIL_TEST);
951 glStencilMask (0);
952 glStencilFunc (GL_ALWAYS, 0, 0);
954 /* Test the 8 corners of a cube spanning the event */
955 min_depth = -50; /* FIXME */
956 max_depth = 0; /* FIXME */
958 ghid_unproject_to_z_plane (ev->area.x,
959 ev->area.y,
960 min_depth, &new_x, &new_y);
961 max_x = min_x = new_x;
962 max_y = min_y = new_y;
964 ghid_unproject_to_z_plane (ev->area.x,
965 ev->area.y,
966 max_depth, &new_x, &new_y);
967 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
968 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
970 /* */
971 ghid_unproject_to_z_plane (ev->area.x + ev->area.width,
972 ev->area.y,
973 min_depth, &new_x, &new_y);
974 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
975 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
977 ghid_unproject_to_z_plane (ev->area.x + ev->area.width, ev->area.y,
978 max_depth, &new_x, &new_y);
979 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
980 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
982 /* */
983 ghid_unproject_to_z_plane (ev->area.x + ev->area.width,
984 ev->area.y + ev->area.height,
985 min_depth, &new_x, &new_y);
986 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
987 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
989 ghid_unproject_to_z_plane (ev->area.x + ev->area.width,
990 ev->area.y + ev->area.height,
991 max_depth, &new_x, &new_y);
992 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
993 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
995 /* */
996 ghid_unproject_to_z_plane (ev->area.x,
997 ev->area.y + ev->area.height,
998 min_depth,
999 &new_x, &new_y);
1000 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1001 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1003 ghid_unproject_to_z_plane (ev->area.x,
1004 ev->area.y + ev->area.height,
1005 max_depth,
1006 &new_x, &new_y);
1007 min_x = MIN (min_x, new_x); max_x = MAX (max_x, new_x);
1008 min_y = MIN (min_y, new_y); max_y = MAX (max_y, new_y);
1010 region.X1 = min_x; region.X2 = max_x + 1;
1011 region.Y1 = min_y; region.Y2 = max_y + 1;
1013 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
1014 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
1015 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
1016 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
1018 glColor3f (port->bg_color.red / 65535.,
1019 port->bg_color.green / 65535.,
1020 port->bg_color.blue / 65535.);
1022 glBegin (GL_QUADS);
1023 glVertex3i (0, 0, -50);
1024 glVertex3i (PCB->MaxWidth, 0, -50);
1025 glVertex3i (PCB->MaxWidth, PCB->MaxHeight, -50);
1026 glVertex3i (0, PCB->MaxHeight, -50);
1027 glEnd ();
1029 ghid_draw_bg_image ();
1031 ghid_invalidate_current_gc ();
1032 hid_expose_callback (&ghid_hid, &region, 0);
1033 hidgl_flush_triangles (&buffer);
1035 ghid_graphics.draw_grid (&region);
1037 ghid_invalidate_current_gc ();
1039 DrawAttached (priv->crosshair_gc);
1040 DrawMark (priv->crosshair_gc);
1041 hidgl_flush_triangles (&buffer);
1043 draw_crosshair (priv);
1044 hidgl_flush_triangles (&buffer);
1046 draw_lead_user (priv);
1048 hidgl_finish_render ();
1049 ghid_end_drawing (port, widget);
1051 g_timer_start (priv->time_since_expose);
1053 return FALSE;
1056 /* This realize callback is used to work around a crash bug in some mesa
1057 * versions (observed on a machine running the intel i965 driver. It isn't
1058 * obvious why it helps, but somehow fiddling with the GL context here solves
1059 * the issue. The problem appears to have been fixed in recent mesa versions.
1061 void
1062 ghid_port_drawing_realize_cb (GtkWidget *widget, gpointer data)
1064 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
1065 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
1067 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
1068 return;
1070 gdk_gl_drawable_gl_end (gldrawable);
1071 return;
1074 gboolean
1075 ghid_pinout_preview_expose (GtkWidget *widget,
1076 GdkEventExpose *ev)
1078 GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW (widget);
1079 GtkAllocation allocation;
1080 view_data save_view;
1081 int save_width, save_height;
1082 Coord save_max_width;
1083 Coord save_max_height;
1084 double xz, yz;
1086 save_view = gport->view;
1087 save_width = gport->width;
1088 save_height = gport->height;
1089 save_max_width = PCB->MaxWidth;
1090 save_max_height = PCB->MaxHeight;
1092 /* Setup zoom factor for drawing routines */
1094 gtk_widget_get_allocation (widget, &allocation);
1095 xz = (double) pinout->x_max / allocation.width;
1096 yz = (double) pinout->y_max / allocation.height;
1097 if (xz > yz)
1098 gport->view.coord_per_px = xz;
1099 else
1100 gport->view.coord_per_px = yz;
1102 gport->width = allocation.width;
1103 gport->height = allocation.height;
1104 gport->view.width = allocation.width * gport->view.coord_per_px;
1105 gport->view.height = allocation.height * gport->view.coord_per_px;
1106 gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
1107 gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
1108 PCB->MaxWidth = pinout->x_max;
1109 PCB->MaxHeight = pinout->y_max;
1111 ghid_start_drawing (gport, widget);
1112 hidgl_start_render ();
1114 #if 0 /* We disable alpha blending here, as hid_expose_callback() does not
1115 * call set_layer() as appropriate for us to sub-composite rendering
1116 * from each layer when drawing a single element. If we leave alpha-
1117 * blending on, it means text and overlapping pads are rendered ugly.
1120 glEnable (GL_BLEND);
1121 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1122 #endif
1124 glViewport (0, 0, allocation.width, allocation.height);
1126 #if 0 /* We disable the scissor test here, as it is interacting badly with
1127 * being handed expose events which don't cover the whole window.
1128 * As we have a double-buffered GL window, we end up with unintialised
1129 * contents remaining in the unpainted areas (outside the scissor
1130 * region), and these are being flipped onto the screen.
1132 * The debugging code below shows multiple expose events when the
1133 * window is shown the first time, some of which are very small.
1135 * XXX: There is clearly a perforamnce issue here, in that we may
1136 * be rendering the preview more times, and over a larger area
1137 * than is really required.
1140 glEnable (GL_SCISSOR_TEST);
1141 glScissor (ev->area.x,
1142 allocation.height - ev->area.height - ev->area.y,
1143 ev->area.width, ev->area.height);
1144 #endif
1146 #ifdef DEBUG
1147 printf ("EVT: %i, %i, w=%i, h=%i, Scissor setup: glScissor (%f, %f, %f, %f);\n",
1148 ev->area.x, ev->area.y, ev->area.width, ev->area.height,
1149 (double)ev->area.x,
1150 (double)(allocation.height - ev->area.height - ev->area.y),
1151 (double)ev->area.width,
1152 (double)ev->area.height);
1153 #endif
1155 glMatrixMode (GL_PROJECTION);
1156 glLoadIdentity ();
1157 glOrtho (0, allocation.width, allocation.height, 0, -100000, 100000);
1158 glMatrixMode (GL_MODELVIEW);
1159 glLoadIdentity ();
1160 glTranslatef (0.0f, 0.0f, -Z_NEAR);
1162 glClearColor (gport->bg_color.red / 65535.,
1163 gport->bg_color.green / 65535.,
1164 gport->bg_color.blue / 65535.,
1165 1.);
1166 glStencilMask (~0);
1167 glClearStencil (0);
1168 glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1169 hidgl_reset_stencil_usage ();
1171 /* Disable the stencil test until we need it - otherwise it gets dirty */
1172 glDisable (GL_STENCIL_TEST);
1173 glStencilMask (0);
1174 glStencilFunc (GL_ALWAYS, 0, 0);
1176 /* call the drawing routine */
1177 ghid_invalidate_current_gc ();
1178 glPushMatrix ();
1179 glScalef ((gport->view.flip_x ? -1. : 1.) / gport->view.coord_per_px,
1180 (gport->view.flip_y ? -1. : 1.) / gport->view.coord_per_px,
1181 ((gport->view.flip_x == gport->view.flip_y) ? 1. : -1.) / gport->view.coord_per_px);
1182 glTranslatef (gport->view.flip_x ? gport->view.x0 - PCB->MaxWidth :
1183 -gport->view.x0,
1184 gport->view.flip_y ? gport->view.y0 - PCB->MaxHeight :
1185 -gport->view.y0, 0);
1187 hid_expose_callback (&ghid_hid, NULL, pinout->element);
1188 hidgl_flush_triangles (&buffer);
1189 glPopMatrix ();
1191 hidgl_finish_render ();
1192 ghid_end_drawing (gport, widget);
1194 gport->view = save_view;
1195 gport->width = save_width;
1196 gport->height = save_height;
1197 PCB->MaxWidth = save_max_width;
1198 PCB->MaxHeight = save_max_height;
1200 return FALSE;
1204 GdkPixmap *
1205 ghid_render_pixmap (int cx, int cy, double zoom, int width, int height, int depth)
1207 GdkGLConfig *glconfig;
1208 GdkPixmap *pixmap;
1209 GdkGLPixmap *glpixmap;
1210 GdkGLContext* glcontext;
1211 GdkGLDrawable* gldrawable;
1212 view_data save_view;
1213 int save_width, save_height;
1214 BoxType region;
1216 save_view = gport->view;
1217 save_width = gport->width;
1218 save_height = gport->height;
1220 /* Setup rendering context for drawing routines
1223 glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB |
1224 GDK_GL_MODE_STENCIL |
1225 GDK_GL_MODE_SINGLE);
1227 pixmap = gdk_pixmap_new (NULL, width, height, depth);
1228 glpixmap = gdk_pixmap_set_gl_capability (pixmap, glconfig, NULL);
1229 gldrawable = GDK_GL_DRAWABLE (glpixmap);
1230 glcontext = gdk_gl_context_new (gldrawable, NULL, TRUE, GDK_GL_RGBA_TYPE);
1232 /* Setup zoom factor for drawing routines */
1234 gport->view.coord_per_px = zoom;
1235 gport->width = width;
1236 gport->height = height;
1237 gport->view.width = width * gport->view.coord_per_px;
1238 gport->view.height = height * gport->view.coord_per_px;
1239 gport->view.x0 = gport->view.flip_x ? PCB->MaxWidth - cx : cx;
1240 gport->view.x0 -= gport->view.height / 2;
1241 gport->view.y0 = gport->view.flip_y ? PCB->MaxHeight - cy : cy;
1242 gport->view.y0 -= gport->view.width / 2;
1244 /* make GL-context "current" */
1245 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) {
1246 return NULL;
1248 hidgl_start_render ();
1249 gport->render_priv->in_context = true;
1251 glEnable (GL_BLEND);
1252 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1254 glViewport (0, 0, width, height);
1256 glEnable (GL_SCISSOR_TEST);
1257 glScissor (0, 0, width, height);
1259 glMatrixMode (GL_PROJECTION);
1260 glLoadIdentity ();
1261 glOrtho (0, width, height, 0, -100000, 100000);
1262 glMatrixMode (GL_MODELVIEW);
1263 glLoadIdentity ();
1264 glTranslatef (0.0f, 0.0f, -Z_NEAR);
1266 glClearColor (gport->bg_color.red / 65535.,
1267 gport->bg_color.green / 65535.,
1268 gport->bg_color.blue / 65535.,
1269 1.);
1270 glStencilMask (~0);
1271 glClearStencil (0);
1272 glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1273 hidgl_reset_stencil_usage ();
1275 /* Disable the stencil test until we need it - otherwise it gets dirty */
1276 glDisable (GL_STENCIL_TEST);
1277 glStencilMask (0);
1278 glStencilFunc (GL_ALWAYS, 0, 0);
1280 /* call the drawing routine */
1281 ghid_invalidate_current_gc ();
1282 glPushMatrix ();
1283 glScalef ((gport->view.flip_x ? -1. : 1.) / gport->view.coord_per_px,
1284 (gport->view.flip_y ? -1. : 1.) / gport->view.coord_per_px,
1285 ((gport->view.flip_x == gport->view.flip_y) ? 1. : -1.) / gport->view.coord_per_px);
1286 glTranslatef (gport->view.flip_x ? gport->view.x0 - PCB->MaxWidth :
1287 -gport->view.x0,
1288 gport->view.flip_y ? gport->view.y0 - PCB->MaxHeight :
1289 -gport->view.y0, 0);
1291 region.X1 = MIN(Px(0), Px(gport->width + 1));
1292 region.Y1 = MIN(Py(0), Py(gport->height + 1));
1293 region.X2 = MAX(Px(0), Px(gport->width + 1));
1294 region.Y2 = MAX(Py(0), Py(gport->height + 1));
1296 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
1297 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
1298 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
1299 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
1301 hid_expose_callback (&ghid_hid, &region, NULL);
1302 hidgl_flush_triangles (&buffer);
1303 glPopMatrix ();
1305 glFlush ();
1307 hidgl_finish_render ();
1309 /* end drawing to current GL-context */
1310 gport->render_priv->in_context = false;
1311 gdk_gl_drawable_gl_end (gldrawable);
1313 gdk_pixmap_unset_gl_capability (pixmap);
1315 g_object_unref (glconfig);
1316 g_object_unref (glcontext);
1318 gport->view = save_view;
1319 gport->width = save_width;
1320 gport->height = save_height;
1322 return pixmap;
1325 HID_DRAW *
1326 ghid_request_debug_draw (void)
1328 GHidPort *port = gport;
1329 GtkWidget *widget = port->drawing_area;
1330 GtkAllocation allocation;
1332 gtk_widget_get_allocation (widget, &allocation);
1334 ghid_start_drawing (port, widget);
1335 hidgl_start_render ();
1337 glViewport (0, 0, allocation.width, allocation.height);
1339 glMatrixMode (GL_PROJECTION);
1340 glLoadIdentity ();
1341 glOrtho (0, allocation.width, allocation.height, 0, 0, 100);
1342 glMatrixMode (GL_MODELVIEW);
1343 glLoadIdentity ();
1344 glTranslatef (0.0f, 0.0f, -Z_NEAR);
1346 ghid_invalidate_current_gc ();
1348 /* Setup stenciling */
1349 glDisable (GL_STENCIL_TEST);
1351 glPushMatrix ();
1352 glScalef ((port->view.flip_x ? -1. : 1.) / port->view.coord_per_px,
1353 (port->view.flip_y ? -1. : 1.) / port->view.coord_per_px,
1354 ((gport->view.flip_x == port->view.flip_y) ? 1. : -1.) / gport->view.coord_per_px);
1355 glTranslatef (port->view.flip_x ? port->view.x0 - PCB->MaxWidth :
1356 -port->view.x0,
1357 port->view.flip_y ? port->view.y0 - PCB->MaxHeight :
1358 -port->view.y0, 0);
1360 return ghid_hid.graphics;
1363 void
1364 ghid_flush_debug_draw (void)
1366 GtkWidget *widget = gport->drawing_area;
1367 GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable (widget);
1369 hidgl_flush_triangles (&buffer);
1371 if (gdk_gl_drawable_is_double_buffered (pGlDrawable))
1372 gdk_gl_drawable_swap_buffers (pGlDrawable);
1373 else
1374 glFlush ();
1377 void
1378 ghid_finish_debug_draw (void)
1380 hidgl_flush_triangles (&buffer);
1381 glPopMatrix ();
1383 hidgl_finish_render ();
1384 ghid_end_drawing (gport, gport->drawing_area);
1387 static float
1388 determinant_2x2 (float m[2][2])
1390 float det;
1391 det = m[0][0] * m[1][1] -
1392 m[0][1] * m[1][0];
1393 return det;
1396 #if 0
1397 static float
1398 determinant_4x4 (float m[4][4])
1400 float det;
1401 det = m[0][3] * m[1][2] * m[2][1] * m[3][0]-m[0][2] * m[1][3] * m[2][1] * m[3][0] -
1402 m[0][3] * m[1][1] * m[2][2] * m[3][0]+m[0][1] * m[1][3] * m[2][2] * m[3][0] +
1403 m[0][2] * m[1][1] * m[2][3] * m[3][0]-m[0][1] * m[1][2] * m[2][3] * m[3][0] -
1404 m[0][3] * m[1][2] * m[2][0] * m[3][1]+m[0][2] * m[1][3] * m[2][0] * m[3][1] +
1405 m[0][3] * m[1][0] * m[2][2] * m[3][1]-m[0][0] * m[1][3] * m[2][2] * m[3][1] -
1406 m[0][2] * m[1][0] * m[2][3] * m[3][1]+m[0][0] * m[1][2] * m[2][3] * m[3][1] +
1407 m[0][3] * m[1][1] * m[2][0] * m[3][2]-m[0][1] * m[1][3] * m[2][0] * m[3][2] -
1408 m[0][3] * m[1][0] * m[2][1] * m[3][2]+m[0][0] * m[1][3] * m[2][1] * m[3][2] +
1409 m[0][1] * m[1][0] * m[2][3] * m[3][2]-m[0][0] * m[1][1] * m[2][3] * m[3][2] -
1410 m[0][2] * m[1][1] * m[2][0] * m[3][3]+m[0][1] * m[1][2] * m[2][0] * m[3][3] +
1411 m[0][2] * m[1][0] * m[2][1] * m[3][3]-m[0][0] * m[1][2] * m[2][1] * m[3][3] -
1412 m[0][1] * m[1][0] * m[2][2] * m[3][3]+m[0][0] * m[1][1] * m[2][2] * m[3][3];
1413 return det;
1415 #endif
1417 static void
1418 invert_2x2 (float m[2][2], float out[2][2])
1420 float scale = 1 / determinant_2x2 (m);
1421 out[0][0] = m[1][1] * scale;
1422 out[0][1] = -m[0][1] * scale;
1423 out[1][0] = -m[1][0] * scale;
1424 out[1][1] = m[0][0] * scale;
1427 #if 0
1428 static void
1429 invert_4x4 (float m[4][4], float out[4][4])
1431 float scale = 1 / determinant_4x4 (m);
1433 out[0][0] = (m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] +
1434 m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] -
1435 m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3]) * scale;
1436 out[0][1] = (m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] -
1437 m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] +
1438 m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3]) * scale;
1439 out[0][2] = (m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] +
1440 m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] -
1441 m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3]) * scale;
1442 out[0][3] = (m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] -
1443 m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] +
1444 m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3]) * scale;
1445 out[1][0] = (m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] -
1446 m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] +
1447 m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3]) * scale;
1448 out[1][1] = (m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] +
1449 m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] -
1450 m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3]) * scale;
1451 out[1][2] = (m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] -
1452 m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] +
1453 m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3]) * scale;
1454 out[1][3] = (m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] +
1455 m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] -
1456 m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3]) * scale;
1457 out[2][0] = (m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] +
1458 m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] -
1459 m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3]) * scale;
1460 out[2][1] = (m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] -
1461 m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] +
1462 m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3]) * scale;
1463 out[2][2] = (m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] +
1464 m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] -
1465 m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3]) * scale;
1466 out[2][3] = (m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] -
1467 m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] +
1468 m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3]) * scale;
1469 out[3][0] = (m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] -
1470 m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] +
1471 m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2]) * scale;
1472 out[3][1] = (m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] +
1473 m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] -
1474 m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2]) * scale;
1475 out[3][2] = (m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] -
1476 m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] +
1477 m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2]) * scale;
1478 out[3][3] = (m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] +
1479 m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] -
1480 m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) * scale;
1482 #endif
1485 static void
1486 ghid_unproject_to_z_plane (int ex, int ey, Coord pcb_z, Coord *pcb_x, Coord *pcb_y)
1488 float mat[2][2];
1489 float inv_mat[2][2];
1490 float x, y;
1493 ex = view_matrix[0][0] * vx +
1494 view_matrix[0][1] * vy +
1495 view_matrix[0][2] * vz +
1496 view_matrix[0][3] * 1;
1497 ey = view_matrix[1][0] * vx +
1498 view_matrix[1][1] * vy +
1499 view_matrix[1][2] * vz +
1500 view_matrix[1][3] * 1;
1501 UNKNOWN ez = view_matrix[2][0] * vx +
1502 view_matrix[2][1] * vy +
1503 view_matrix[2][2] * vz +
1504 view_matrix[2][3] * 1;
1506 ex - view_matrix[0][3] * 1
1507 - view_matrix[0][2] * vz
1508 = view_matrix[0][0] * vx +
1509 view_matrix[0][1] * vy;
1511 ey - view_matrix[1][3] * 1
1512 - view_matrix[1][2] * vz
1513 = view_matrix[1][0] * vx +
1514 view_matrix[1][1] * vy;
1517 /* NB: last_modelview_matrix is transposed in memory! */
1518 x = (float)ex - last_modelview_matrix[3][0] * 1
1519 - last_modelview_matrix[2][0] * pcb_z;
1521 y = (float)ey - last_modelview_matrix[3][1] * 1
1522 - last_modelview_matrix[2][1] * pcb_z;
1525 x = view_matrix[0][0] * vx +
1526 view_matrix[0][1] * vy;
1528 y = view_matrix[1][0] * vx +
1529 view_matrix[1][1] * vy;
1531 [view_matrix[0][0] view_matrix[0][1]] [vx] = [x]
1532 [view_matrix[1][0] view_matrix[1][1]] [vy] [y]
1535 mat[0][0] = last_modelview_matrix[0][0];
1536 mat[0][1] = last_modelview_matrix[1][0];
1537 mat[1][0] = last_modelview_matrix[0][1];
1538 mat[1][1] = last_modelview_matrix[1][1];
1540 /* if (determinant_2x2 (mat) < 0.00001) */
1541 /* printf ("Determinant is quite small\n"); */
1543 invert_2x2 (mat, inv_mat);
1545 *pcb_x = (int)(inv_mat[0][0] * x + inv_mat[0][1] * y);
1546 *pcb_y = (int)(inv_mat[1][0] * x + inv_mat[1][1] * y);
1550 bool
1551 ghid_event_to_pcb_coords (int event_x, int event_y, Coord *pcb_x, Coord *pcb_y)
1553 ghid_unproject_to_z_plane (event_x, event_y, 0, pcb_x, pcb_y);
1555 return true;
1558 bool
1559 ghid_pcb_to_event_coords (Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
1561 /* NB: last_modelview_matrix is transposed in memory */
1562 float w = last_modelview_matrix[0][3] * (float)pcb_x +
1563 last_modelview_matrix[1][3] * (float)pcb_y +
1564 last_modelview_matrix[2][3] * 0. +
1565 last_modelview_matrix[3][3] * 1.;
1567 *event_x = (last_modelview_matrix[0][0] * (float)pcb_x +
1568 last_modelview_matrix[1][0] * (float)pcb_y +
1569 last_modelview_matrix[2][0] * 0. +
1570 last_modelview_matrix[3][0] * 1.) / w;
1571 *event_y = (last_modelview_matrix[0][1] * (float)pcb_x +
1572 last_modelview_matrix[1][1] * (float)pcb_y +
1573 last_modelview_matrix[2][1] * 0. +
1574 last_modelview_matrix[3][1] * 1.) / w;
1576 return true;
1579 void
1580 ghid_view_2d (void *ball, gboolean view_2d, gpointer userdata)
1582 global_view_2d = view_2d;
1583 ghid_invalidate_all ();
1586 void
1587 ghid_port_rotate (void *ball, float *quarternion, gpointer userdata)
1589 #ifdef DEBUG_ROTATE
1590 int row, column;
1591 #endif
1593 build_rotmatrix (view_matrix, quarternion);
1595 #ifdef DEBUG_ROTATE
1596 for (row = 0; row < 4; row++) {
1597 printf ("[ %f", view_matrix[row][0]);
1598 for (column = 1; column < 4; column++) {
1599 printf (",\t%f", view_matrix[row][column]);
1601 printf ("\t]\n");
1603 printf ("\n");
1604 #endif
1606 ghid_invalidate_all ();
1610 #define LEAD_USER_WIDTH 0.2 /* millimeters */
1611 #define LEAD_USER_PERIOD (1000 / 20) /* 20fps (in ms) */
1612 #define LEAD_USER_VELOCITY 3. /* millimeters per second */
1613 #define LEAD_USER_ARC_COUNT 3
1614 #define LEAD_USER_ARC_SEPARATION 3. /* millimeters */
1615 #define LEAD_USER_INITIAL_RADIUS 10. /* millimetres */
1616 #define LEAD_USER_COLOR_R 1.
1617 #define LEAD_USER_COLOR_G 1.
1618 #define LEAD_USER_COLOR_B 0.
1620 static void
1621 draw_lead_user (render_priv *priv)
1623 int i;
1624 double radius = priv->lead_user_radius;
1625 double width = MM_TO_COORD (LEAD_USER_WIDTH);
1626 double separation = MM_TO_COORD (LEAD_USER_ARC_SEPARATION);
1628 if (!priv->lead_user)
1629 return;
1631 glPushAttrib (GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT);
1632 glEnable (GL_COLOR_LOGIC_OP);
1633 glLogicOp (GL_XOR);
1634 glColor3f (LEAD_USER_COLOR_R, LEAD_USER_COLOR_G,LEAD_USER_COLOR_B);
1637 /* arcs at the approrpriate radii */
1639 for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation)
1641 if (radius < width)
1642 radius += MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1644 /* Draw an arc at radius */
1645 hidgl_draw_arc (width, priv->lead_user_x, priv->lead_user_y,
1646 radius, radius, 0, 360, gport->view.coord_per_px);
1649 hidgl_flush_triangles (&buffer);
1650 glPopAttrib ();
1653 gboolean
1654 lead_user_cb (gpointer data)
1656 render_priv *priv = data;
1657 Coord step;
1658 double elapsed_time;
1660 /* Queue a redraw */
1661 ghid_invalidate_all ();
1663 /* Update radius */
1664 elapsed_time = g_timer_elapsed (priv->lead_user_timer, NULL);
1665 g_timer_start (priv->lead_user_timer);
1667 step = MM_TO_COORD (LEAD_USER_VELOCITY * elapsed_time);
1668 if (priv->lead_user_radius > step)
1669 priv->lead_user_radius -= step;
1670 else
1671 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1673 return TRUE;
1676 void
1677 ghid_lead_user_to_location (Coord x, Coord y)
1679 render_priv *priv = gport->render_priv;
1681 ghid_cancel_lead_user ();
1683 priv->lead_user = true;
1684 priv->lead_user_x = x;
1685 priv->lead_user_y = y;
1686 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1687 priv->lead_user_timeout = g_timeout_add (LEAD_USER_PERIOD, lead_user_cb, priv);
1688 priv->lead_user_timer = g_timer_new ();
1691 void
1692 ghid_cancel_lead_user (void)
1694 render_priv *priv = gport->render_priv;
1696 if (priv->lead_user_timeout)
1697 g_source_remove (priv->lead_user_timeout);
1699 if (priv->lead_user_timer)
1700 g_timer_destroy (priv->lead_user_timer);
1702 if (priv->lead_user)
1703 ghid_invalidate_all ();
1705 priv->lead_user_timeout = 0;
1706 priv->lead_user_timer = NULL;
1707 priv->lead_user = false;