1 /* brush.c */
2 /*
3 * ggobi
4 * Copyright (C) AT&T, Duncan Temple Lang, Dianne Cook 1999-2005
5 *
6 * ggobi is free software; you may use, redistribute, and/or modify it
7 * under the terms of the Eclipse Public License, which is distributed
8 * with the source code and displayed on the ggobi web site,
9 * www.ggobi.org. For more information, contact the authors:
10 *
11 * Deborah F. Swayne dfs@research.att.com
12 * Di Cook dicook@iastate.edu
13 * Duncan Temple Lang duncan@wald.ucdavis.edu
14 * Andreas Buja andreas.buja@wharton.upenn.edu
15 */
16
17 #include <string.h>
18 #include <stdlib.h>
19 #include <math.h>
20
21 #include <gtk/gtk.h>
22 #include "vars.h"
23 #include "externs.h"
24
25 /* */
26 static gboolean active_paint_points (splotd *, GGobiData * d, ggobid * gg);
27 static gboolean active_paint_edges (splotd *, GGobiData * e, ggobid * gg);
28 static gboolean build_symbol_vectors (cpaneld *, GGobiData *, ggobid *);
29 /* */
30
31 /*----------------------------------------------------------------------*/
32 /* Glyph utility: called in read_data */
33 /*----------------------------------------------------------------------*/
34
35 /* convert a glyph size into a line width */
36 gint
lwidth_from_gsize(gint size)37 lwidth_from_gsize (gint size)
38 {
39 return ((size < 3) ? 0 : (size - 2) * 2);
40 }
41
42 /* This refers to the stored ltype, apparently */
43 gint
ltype_from_gtype(gint gtype)44 ltype_from_gtype (gint gtype)
45 {
46 gint ltype;
47
48 /* These ltypes are the three EDGETYPES; should be an enum */
49
50 if (gtype == FC || gtype == FR)
51 ltype = SOLID;
52 else if (gtype == OC || gtype == OR)
53 ltype = WIDE_DASH;
54 else
55 ltype = NARROW_DASH;
56
57 return ltype;
58 }
59
60 /* sets dashes and returns a gtk line attribute */
61 gint
set_lattribute_from_ltype(gint ltype,ggobid * gg)62 set_lattribute_from_ltype (gint ltype, ggobid * gg)
63 {
64 gint8 dash_list[2];
65 gint lattr = GDK_LINE_SOLID;
66
67 switch (ltype) { /* one of the three EDGETYPES; should be an enum */
68 case SOLID:
69 lattr = GDK_LINE_SOLID;
70 break;
71 case WIDE_DASH:
72 lattr = GDK_LINE_ON_OFF_DASH;
73 dash_list[0] = 8;
74 dash_list[1] = 2;
75 gdk_gc_set_dashes (gg->plot_GC, 0, dash_list, 2);
76 break;
77 case NARROW_DASH:
78 lattr = GDK_LINE_ON_OFF_DASH;
79 dash_list[0] = 4;
80 dash_list[1] = 2;
81 gdk_gc_set_dashes (gg->plot_GC, 0, dash_list, 2);
82 break;
83 }
84 return lattr;
85 }
86
87 void
find_glyph_type_and_size(gint gid,glyphd * glyph)88 find_glyph_type_and_size (gint gid, glyphd * glyph)
89 {
90 /* gid ranges from 0:42, type from 0 to 6, size from 0 to 7 */
91 if (gid == 0) {
92 glyph->type = glyph->size = 0; /*-- single-pixel point --*/
93 }
94 else {
95 glyph->type = ((gid - 1) / (gint) (NGLYPHSIZES)) + 1;
96 glyph->size = ((gid - 1) % (gint) (NGLYPHSIZES));
97 }
98 }
99
100 /*----------------------------------------------------------------------*/
101
102 /*-- called by brush_motion, brush_mode_cb, and in the api --*/
103 gboolean
brush_once(gboolean force,splotd * sp,ggobid * gg)104 brush_once (gboolean force, splotd * sp, ggobid * gg)
105 {
106 /*
107 * Determine which bins the brush is currently sitting in.
108 * bin0 is the bin which contains of the upper left corner of the
109 * brush; bin1 is the one containing of the lower right corner.
110 */
111 displayd *display = sp->displayptr;
112 GGobiData *d = display->d;
113 GGobiData *e = display->e;
114
115 brush_coords *brush_pos = &sp->brush_pos;
116
117 icoords *bin0 = &d->brush.bin0;
118 icoords *bin1 = &d->brush.bin1;
119
120 gboolean changed = false;
121 cpaneld *cpanel = &display->cpanel;
122
123 if (force) { /*-- force the bin to be the entire screen --*/
124 bin0->x = 0;
125 bin0->y = 0;
126 bin1->x = d->brush.nbins - 1;
127 bin1->y = d->brush.nbins - 1;
128
129 }
130 else {
131
132 gint ulx = MIN (brush_pos->x1, brush_pos->x2);
133 gint uly = MIN (brush_pos->y1, brush_pos->y2);
134 gint lrx = MAX (brush_pos->x1, brush_pos->x2);
135 gint lry = MAX (brush_pos->y1, brush_pos->y2);
136
137 if (!point_in_which_bin (ulx, uly, &bin0->x, &bin0->y, d, sp)) {
138 bin0->x = MAX (bin0->x, 0);
139 bin0->x = MIN (bin0->x, d->brush.nbins - 1);
140 bin0->y = MAX (bin0->y, 0);
141 bin0->y = MIN (bin0->y, d->brush.nbins - 1);
142 }
143 if (!point_in_which_bin (lrx, lry, &bin1->x, &bin1->y, d, sp)) {
144 bin1->x = MAX (bin1->x, 0);
145 bin1->x = MIN (bin1->x, d->brush.nbins - 1);
146 bin1->y = MAX (bin1->y, 0);
147 bin1->y = MIN (bin1->y, d->brush.nbins - 1);
148 }
149 }
150
151 /*
152 * Now paint.
153 */
154 if (!cpanel->br.edge_targets /*cpanel->br.point_targets*/) {
155 changed = active_paint_points (sp, d, gg);
156 }
157
158 if (cpanel->br.edge_targets) {
159 if (e != NULL)
160 changed = active_paint_edges (sp, e, gg);
161 }
162
163 return (changed);
164 }
165
166 void
brush_prev_vectors_update(GGobiData * d,ggobid * gg)167 brush_prev_vectors_update (GGobiData * d, ggobid * gg)
168 {
169 gint m, i;
170
171 g_assert (d->color.nels == d->nrows);
172
173 if (d->color_prev.nels < d->nrows) {
174 vectors_realloc (&d->color_prev, d->nrows);
175 vectorb_realloc (&d->hidden_prev, d->nrows);
176 vectorg_realloc (&d->glyph_prev, d->nrows);
177 }
178
179 for (m = 0; m < d->nrows_in_plot; m++) {
180 i = d->rows_in_plot.els[m];
181 d->color_prev.els[i] = d->color.els[i];
182 d->hidden_prev.els[i] = d->hidden.els[i];
183 d->glyph_prev.els[i].size = d->glyph.els[i].size;
184 d->glyph_prev.els[i].type = d->glyph.els[i].type;
185 }
186 }
187
188 void
brush_undo(splotd * sp,GGobiData * d,ggobid * gg)189 brush_undo (splotd * sp, GGobiData * d, ggobid * gg)
190 {
191 gint m, i;
192 if (!d)
193 return;
194
195 g_assert (d->color.nels == d->nrows);
196
197 for (m = 0; m < d->nrows_in_plot; m++) {
198 i = d->rows_in_plot.els[m];
199 d->color.els[i] = d->color_now.els[i] = d->color_prev.els[i];
200 d->hidden.els[i] = d->hidden_now.els[i] = d->hidden_prev.els[i];
201 d->glyph.els[i].type = d->glyph_now.els[i].type =
202 d->glyph_prev.els[i].type;
203 d->glyph.els[i].size = d->glyph_now.els[i].size =
204 d->glyph_prev.els[i].size;
205 }
206 }
207
208 void
reinit_transient_brushing(displayd * dsp,ggobid * gg)209 reinit_transient_brushing (displayd * dsp, ggobid * gg)
210 {
211 /*
212 * If a new variable is selected or a variable is transformed
213 * during transient brushing, we may want to restore all points to
214 * the permanent value. After calling this routine, re-execute
215 * brush_once() to brush the points that are now underneath the brush.
216 * For now, don't make the same change for persistent brushing.
217 */
218 gint i, m, k;
219 GGobiData *d = dsp->d;
220 GGobiData *e = dsp->e;
221 cpaneld *cpanel = &dsp->cpanel;
222 gboolean point_painting_p = (cpanel->br.point_targets != br_off);
223 gboolean edge_painting_p = (cpanel->br.edge_targets != br_off);
224
225 g_assert (d->color.nels == d->nrows);
226
227 if (point_painting_p) {
228 for (m = 0; m < d->nrows_in_plot; m++) {
229 i = d->rows_in_plot.els[m];
230 d->color_now.els[i] = d->color.els[i];
231 d->glyph_now.els[i].type = d->glyph.els[i].type;
232 d->glyph_now.els[i].size = d->glyph.els[i].size;
233 d->hidden_now.els[i] = d->hidden.els[i];
234 }
235 }
236 if (edge_painting_p && e != NULL) {
237 for (k = 0; k < e->edge.n; k++) {
238 e->color_now.els[k] = e->color.els[k];
239 e->glyph_now.els[k].type = e->glyph.els[k].type;
240 e->glyph_now.els[k].size = e->glyph.els[k].size;
241 e->hidden_now.els[k] = e->hidden.els[k];
242 }
243 }
244 }
245
246 void
brush_set_pos(gint x,gint y,splotd * sp)247 brush_set_pos (gint x, gint y, splotd * sp)
248 {
249 brush_coords *brush = &sp->brush_pos;
250 brush_coords *obrush = &sp->brush_pos_o;
251 gint xdist = brush->x2 - brush->x1;
252 gint ydist = brush->y2 - brush->y1;
253
254 /*-- copy the current coordinates to the backup brush structure --*/
255 obrush->x1 = brush->x1;
256 obrush->y1 = brush->y1;
257 obrush->x2 = brush->x2;
258 obrush->y2 = brush->y2;
259
260 /*
261 * (x2,y2) is the corner that's moving.
262 */
263 brush->x1 = x - xdist;
264 brush->x2 = x;
265 brush->y1 = y - ydist;
266 brush->y2 = y;
267 }
268
269 static gboolean
binning_permitted(displayd * display,ggobid * gg)270 binning_permitted (displayd * display, ggobid * gg)
271 {
272 GGobiData *e = display->e;
273 gboolean permitted = true;
274
275 if (gg->linkby_cv)
276 return (false);
277
278 if (GGOBI_IS_EXTENDED_DISPLAY (display)) {
279 /* If there is a function to determine this, call it. Otherwise just
280 get the value of binning_ok in the class. */
281 GGobiExtendedDisplayClass *klass;
282 klass = GGOBI_EXTENDED_DISPLAY_GET_CLASS (display);
283 if (klass->binningPermitted) {
284 return (klass->binningPermitted (display));
285 }
286 return (klass->binning_ok);
287 }
288
289
290 /*-- if we're drawing edges --*/
291 if (e != NULL && e->edge.n > 0) {
292 if (display->options.edges_undirected_show_p ||
293 display->options.edges_directed_show_p ||
294 display->options.whiskers_show_p) {
295 permitted = false;
296 }
297 }
298
299 return permitted;
300 }
301
302 gboolean
brush_once_and_redraw(gboolean binningp,splotd * sp,displayd * display,ggobid * gg)303 brush_once_and_redraw (gboolean binningp, splotd * sp, displayd * display,
304 ggobid * gg)
305 {
306 cpaneld *cpanel = &display->cpanel;
307 gboolean changed = false;
308
309 if (cpanel->br.brush_on_p) {
310 changed = brush_once (!binningp, sp, gg);
311
312 if (binningp && binning_permitted (display, gg)) {
313 if (changed) {
314 splot_redraw (sp, BINNED, gg);
315 if (cpanel->br.updateAlways_p) {
316 displays_plot (sp, FULL, gg);
317 }
318
319 }
320 else { /*-- just redraw the brush --*/
321 splot_redraw (sp, QUICK, gg);
322 }
323
324 }
325 else { /* no binning */
326 splot_redraw (sp, FULL, gg);
327 if (cpanel->br.updateAlways_p)
328 displays_plot (sp, FULL, gg);
329 }
330
331 }
332 else { /*-- we're not brushing, and we just need to redraw the brush --*/
333 splot_redraw (sp, QUICK, gg);
334 }
335
336 return (changed);
337 }
338
339 gboolean
brush_motion(icoords * mouse,gboolean button1_p,gboolean button2_p,cpaneld * cpanel,splotd * sp,ggobid * gg)340 brush_motion (icoords * mouse, gboolean button1_p, gboolean button2_p,
341 cpaneld * cpanel, splotd * sp, ggobid * gg)
342 {
343 displayd *display = sp->displayptr;
344 brush_coords *brush_pos = &sp->brush_pos;
345
346 if (button1_p) {
347 brush_set_pos (mouse->x, mouse->y, sp);
348 }
349 else if (button2_p) {
350 brush_pos->x2 = mouse->x;
351 brush_pos->y2 = mouse->y;
352 }
353
354 return (brush_once_and_redraw (true, sp, display, gg)); /* binning permitted */
355 }
356
357
358 static gboolean
under_brush(gint k,splotd * sp)359 under_brush (gint k, splotd * sp)
360 /*
361 * Determine whether point k is under the brush.
362 */
363 {
364 brush_coords *brush_pos = &sp->brush_pos;
365 gint pt;
366 gint x1 = MIN (brush_pos->x1, brush_pos->x2);
367 gint x2 = MAX (brush_pos->x1, brush_pos->x2);
368 gint y1 = MIN (brush_pos->y1, brush_pos->y2);
369 gint y2 = MAX (brush_pos->y1, brush_pos->y2);
370
371 pt = (sp->screen[k].x <= x2 && sp->screen[k].y <= y2 &&
372 sp->screen[k].x >= x1 && sp->screen[k].y >= y1) ? 1 : 0;
373 return (pt);
374 }
375
376
377 /*----------------------------------------------------------------------*/
378 /* Dealing with the brush */
379 /*----------------------------------------------------------------------*/
380
381 static void
brush_boundaries_set(cpaneld * cpanel,icoords * obin0,icoords * obin1,icoords * imin,icoords * imax,GGobiData * d,ggobid * gg)382 brush_boundaries_set (cpaneld * cpanel,
383 icoords * obin0, icoords * obin1,
384 icoords * imin, icoords * imax, GGobiData * d,
385 ggobid * gg)
386 {
387 icoords *bin0 = &d->brush.bin0;
388 icoords *bin1 = &d->brush.bin1;
389
390 if (cpanel->br.mode == BR_TRANSIENT) {
391 imin->x = MIN (bin0->x, obin0->x);
392 imin->y = MIN (bin0->y, obin0->y);
393 imax->x = MAX (bin1->x, obin1->x);
394 imax->y = MAX (bin1->y, obin1->y);
395 }
396 else {
397 imin->x = bin0->x;
398 imin->y = bin0->y;
399 imax->x = bin1->x;
400 imax->y = bin1->y;
401 }
402 }
403
404 void
brush_draw_label(splotd * sp,GdkDrawable * drawable,GGobiData * d,ggobid * gg)405 brush_draw_label (splotd * sp, GdkDrawable * drawable, GGobiData * d,
406 ggobid * gg)
407 {
408 PangoRectangle rect;
409 PangoLayout *layout =
410 gtk_widget_create_pango_layout (GTK_WIDGET (sp->da), NULL);
411
412 if (d->npts_under_brush > 0) {
413 gchar *str = g_strdup_printf ("%d", d->npts_under_brush);
414 layout_text (layout, str, &rect);
415 gdk_draw_layout (drawable, gg->plot_GC,
416 sp->max.x - rect.width - 5, 5, layout);
417 g_free (str);
418 }
419 g_object_unref (G_OBJECT (layout));
420 }
421
422 void
brush_draw_brush(splotd * sp,GdkDrawable * drawable,GGobiData * d,ggobid * gg)423 brush_draw_brush (splotd * sp, GdkDrawable * drawable, GGobiData * d,
424 ggobid * gg)
425 {
426 /*
427 * Use brush_pos to draw the brush.
428 */
429 displayd *display = sp->displayptr;
430 cpaneld *cpanel = &display->cpanel;
431 gboolean point_painting_p = (cpanel->br.point_targets != br_off);
432 gboolean edge_painting_p = (cpanel->br.edge_targets != br_off);
433 gboolean selection_p = !point_painting_p && !edge_painting_p;
434 colorschemed *scheme = gg->activeColorScheme;
435
436 brush_coords *brush_pos = &sp->brush_pos;
437 gint x1 = MIN (brush_pos->x1, brush_pos->x2);
438 gint x2 = MAX (brush_pos->x1, brush_pos->x2);
439 gint y1 = MIN (brush_pos->y1, brush_pos->y2);
440 gint y2 = MAX (brush_pos->y1, brush_pos->y2);
441
442 if (cpanel->br.mode == BR_TRANSIENT) {
443 gint8 dash_list[2];
444 gdk_gc_set_line_attributes (gg->plot_GC,
445 0, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND,
446 GDK_JOIN_ROUND);
447 /* just selection: set special dash pattern and draw like points */
448 if (selection_p) {
449 dash_list[0] = 2;
450 dash_list[1] = 2;
451 } else { /* otherwise set to default */
452 dash_list[0] = 4;
453 dash_list[1] = 4;
454 }
455 gdk_gc_set_dashes(gg->plot_GC, 0, dash_list, 2);
456 }
457
458 if (point_painting_p || selection_p) {
459
460 /* set brush color for points */
461 if (cpanel->br.point_targets == br_shadow) {
462 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb_hidden);
463 }
464 else if (cpanel->br.point_targets == br_unshadow) {
465 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb_accent);
466 }
467 else if ((scheme->rgb[gg->color_id].red != scheme->rgb_bg.red ||
468 scheme->rgb[gg->color_id].blue != scheme->rgb_bg.blue ||
469 scheme->rgb[gg->color_id].green != scheme->rgb_bg.green) &&
470 !selection_p) {
471 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb[gg->color_id]);
472 }
473 else {
474 /* for selection (at least) */
475 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb_accent);
476 }
477
478 gdk_draw_rectangle (drawable, gg->plot_GC, false,
479 x1, y1, (x2 > x1) ? (x2 - x1) : (x1 - x2),
480 (y2 > y1) ? (y2 - y1) : (y1 - y2));
481 /* Mark the corner to which the cursor will be attached */
482 gdk_draw_rectangle (drawable, gg->plot_GC, true,
483 brush_pos->x2 - 1, brush_pos->y2 - 1, 2, 2);
484
485 /*
486 * highlight brush: but only in the current display
487 */
488 if (cpanel->br.brush_on_p && display == gg->current_display) {
489 gdk_draw_rectangle (drawable, gg->plot_GC, false,
490 x1 - 1, y1 - 1,
491 (x2 > x1) ? (x2 - x1 + 2) : (x1 - x2 + 2),
492 (y2 > y1) ? (y2 - y1 + 2) : (y1 - y2 + 2));
493
494 /* Mark the corner to which the cursor will be attached */
495 gdk_draw_rectangle (drawable, gg->plot_GC, true,
496 brush_pos->x2 - 2, brush_pos->y2 - 2, 4, 4);
497 }
498 }
499
500 if (edge_painting_p) {
501
502 /* set brush color for edges */
503 if (cpanel->br.edge_targets == br_shadow) {
504 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb_hidden);
505 }
506 else if (cpanel->br.point_targets == br_unshadow) {
507 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb_accent);
508 }
509 else if ((scheme->rgb[gg->color_id].red != scheme->rgb_bg.red) ||
510 (scheme->rgb[gg->color_id].blue != scheme->rgb_bg.blue) ||
511 (scheme->rgb[gg->color_id].green != scheme->rgb_bg.green)) {
512 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb[gg->color_id]);
513 }
514 else {
515 /* I don't remember what this is for ... -- dfs */
516 gdk_gc_set_foreground (gg->plot_GC, &scheme->rgb_accent);
517 }
518
519 gdk_draw_line (drawable, gg->plot_GC,
520 x1 + (x2 - x1) / 2, y1, x1 + (x2 - x1) / 2, y2);
521 gdk_draw_line (drawable, gg->plot_GC,
522 x1, y1 + (y2 - y1) / 2, x2, y1 + (y2 - y1) / 2);
523
524 if (cpanel->br.brush_on_p) {
525 gdk_draw_line (drawable, gg->plot_GC,
526 x1 + (x2 - x1) / 2 + 1, y1, x1 + (x2 - x1) / 2 + 1, y2);
527 gdk_draw_line (drawable, gg->plot_GC,
528 x1, y1 + (y2 - y1) / 2 + 1, x2, y1 + (y2 - y1) / 2 + 1);
529 }
530
531 }
532
533 if (cpanel->br.mode == BR_TRANSIENT)
534 gdk_gc_set_line_attributes (gg->plot_GC,
535 0, GDK_LINE_SOLID, GDK_CAP_ROUND,
536 GDK_JOIN_ROUND);
537 }
538
539 /*----------------------------------------------------------------------*/
540 /* Glyph brushing */
541 /*----------------------------------------------------------------------*/
542
543 /*
544 * This currently isn't using the same arguments as its
545 * _color_ and _hidden_ cousins because we can't brush on line
546 * type yet.
547 */
548 gboolean
update_glyph_vectors(gint i,gboolean changed,gboolean * hit_by_brush,GGobiData * d,ggobid * gg)549 update_glyph_vectors (gint i, gboolean changed, gboolean * hit_by_brush,
550 GGobiData * d, ggobid * gg)
551 {
552 cpaneld *cpanel = &gg->current_display->cpanel;
553 gboolean doit = true;
554
555 /* setting the value of changed */
556 if (!changed) {
557 if (hit_by_brush[i]) {
558 doit = (d->glyph_now.els[i].size != gg->glyph_id.size ||
559 d->glyph_now.els[i].type != gg->glyph_id.type);
560
561 }
562 else {
563 doit = (d->glyph_now.els[i].size != d->glyph.els[i].size ||
564 d->glyph_now.els[i].type != d->glyph.els[i].type);
565 }
566 }
567 /* */
568
569 if (doit) {
570 if (hit_by_brush[i]) {
571 switch (cpanel->br.mode) {
572
573 case BR_PERSISTENT:
574 d->glyph.els[i].size = d->glyph_now.els[i].size = gg->glyph_id.size;
575 d->glyph.els[i].type = d->glyph_now.els[i].type = gg->glyph_id.type;
576 break;
577
578 case BR_TRANSIENT:
579 d->glyph_now.els[i].size = gg->glyph_id.size;
580 d->glyph_now.els[i].type = gg->glyph_id.type;
581 break;
582 }
583 }
584 else {
585 d->glyph_now.els[i].size = d->glyph.els[i].size;
586 d->glyph_now.els[i].type = d->glyph.els[i].type;
587 }
588 }
589
590 return (doit);
591 }
592
593 /*----------------------------------------------------------------------*/
594 /* Color brushing */
595 /*----------------------------------------------------------------------*/
596
597 gboolean
update_color_vectors(gint i,gboolean changed,gboolean * hit_by_brush,GGobiData * d,ggobid * gg)598 update_color_vectors (gint i, gboolean changed, gboolean * hit_by_brush,
599 GGobiData * d, ggobid * gg)
600 {
601 cpaneld *cpanel = &gg->current_display->cpanel;
602 gboolean doit = true;
603
604 /* setting the value of doit */
605 if (!changed) {
606 if (hit_by_brush[i]) {
607 /*-- if persistent, compare against color instead of color_now --*/
608 doit = (cpanel->br.mode == BR_TRANSIENT) ?
609 (d->color_now.els[i] != gg->color_id) :
610 (d->color.els[i] != gg->color_id);
611 } else {
612 doit = (d->color_now.els[i] != d->color.els[i]); /*-- ?? --*/
613 }
614 }
615 /* */
616
617 /*
618 * If doit is false, it's guaranteed that there will be no change.
619 */
620 if (doit) {
621 if (hit_by_brush[i]) {
622 switch (cpanel->br.mode) {
623 case BR_PERSISTENT:
624 d->color.els[i] = d->color_now.els[i] = gg->color_id;
625 break;
626 case BR_TRANSIENT:
627 d->color_now.els[i] = gg->color_id;
628 break;
629 }
630 }
631 else
632 d->color_now.els[i] = d->color.els[i];
633 }
634
635 return (doit);
636 }
637
638 /*----------------------------------------------------------------------*/
639 /* Shadow brushing */
640 /*----------------------------------------------------------------------*/
641
642 gboolean
update_hidden_vectors(gint i,gboolean changed,gboolean * hit_by_brush,GGobiData * d,ggobid * gg)643 update_hidden_vectors (gint i, gboolean changed, gboolean * hit_by_brush,
644 GGobiData * d, ggobid * gg)
645 {
646 cpaneld *cpanel = &gg->current_display->cpanel;
647 gboolean doit = true;
648
649 /*
650 * First find out if this will result in a change; this in
651 * order to be able to return that information.
652 */
653 if (!changed) {
654 if (hit_by_brush[i])
655 /* This test covers the case where a transient brush has first
656 * covered all the points of interest, and then persistent
657 * brushing has been activated. -- dfs */
658 doit = d->hidden_now.els[i] != true ||
659 (cpanel->br.mode == BR_PERSISTENT && d->hidden.els[i] != true);
660 else
661 doit = (d->hidden_now.els[i] != d->hidden.els[i]);
662 }
663 /* */
664
665 /*
666 * If doit is false, it's guaranteed that there will be no change.
667 */
668
669 if (doit) {
670 if (hit_by_brush[i]) {
671 switch (cpanel->br.mode) {
672 case BR_PERSISTENT:
673 d->hidden.els[i] = d->hidden_now.els[i] = true;
674 break;
675 case BR_TRANSIENT:
676 d->hidden_now.els[i] = true;
677 break;
678 }
679 }
680 else
681 d->hidden_now.els[i] = d->hidden.els[i];
682 }
683
684 return (doit);
685 }
686
687 /* This routine named in honor of the Bizarro world of the Superman
688 comics, where everything is backwards. */
689 gboolean
bizarro_update_hidden_vectors(gint i,gboolean changed,gboolean * hit_by_brush,GGobiData * d,ggobid * gg)690 bizarro_update_hidden_vectors (gint i, gboolean changed,
691 gboolean * hit_by_brush, GGobiData * d,
692 ggobid * gg)
693 {
694 cpaneld *cpanel = &gg->current_display->cpanel;
695 gboolean doit = true;
696
697 /*
698 * First find out if this will result in a change; this in
699 * order to be able to return that information.
700 */
701 if (!changed) {
702 if (hit_by_brush[i])
703 /* This test covers the case where a transient brush has first
704 * covered all the points of interest, and then persistent
705 * brushing has been activated. -- dfs */
706 doit = d->hidden_now.els[i] == true ||
707 (cpanel->br.mode == BR_PERSISTENT && d->hidden.els[i] == true);
708 else
709 doit = (d->hidden_now.els[i] != d->hidden.els[i]);
710 }
711 /* */
712
713 /*
714 * If doit is false, it's guaranteed that there will be no change.
715 */
716
717 if (doit) {
718 if (hit_by_brush[i]) {
719 switch (cpanel->br.mode) {
720 case BR_PERSISTENT:
721 d->hidden.els[i] = d->hidden_now.els[i] = false;
722 break;
723 case BR_TRANSIENT:
724 d->hidden_now.els[i] = false;
725 break;
726 }
727 }
728 else {
729 switch (cpanel->br.mode) {
730 case BR_PERSISTENT:
731 d->hidden_now.els[i] = d->hidden.els[i];
732 break;
733 case BR_TRANSIENT:
734 d->hidden_now.els[i] = true;
735 break;
736 }
737 }
738 }
739
740 return (doit);
741 }
742
743
744 /*----------------------------------------------------------------------*/
745 /* Handle all symbols in one loop through a bin */
746 /*----------------------------------------------------------------------*/
747
748 static gboolean
build_symbol_vectors(cpaneld * cpanel,GGobiData * d,ggobid * gg)749 build_symbol_vectors (cpaneld * cpanel, GGobiData * d, ggobid * gg)
750 {
751 gint ih, iv, m, j, k;
752 /*-- these look suspicious -- dfs --*/
753 static icoords obin0 = { BRUSH_NBINS / 2, BRUSH_NBINS / 2 };
754 static icoords obin1 = { BRUSH_NBINS / 2, BRUSH_NBINS / 2 };
755 icoords imin, imax;
756 gboolean changed = false;
757 gint nd = g_slist_length (gg->d);
758 gboolean (*f) (cpaneld *, GGobiData *, ggobid *) = NULL;
759
760 /* These two are needed for the extended display.
761 Should the method be on the extended splot or the display (as it is now).
762 The choice is somewhat arbitrary since the method for the barchart doesn't
763 seem to do much with the display or plot. It is just used to check if we
764 have a barchart. */
765 splotd *sp = gg->current_splot;
766 displayd *display = (displayd *) sp->displayptr;
767
768 if (GGOBI_IS_EXTENDED_DISPLAY (display)) {
769 f = GGOBI_EXTENDED_DISPLAY_GET_CLASS (display)->build_symbol_vectors;
770 if (f) {
771 changed = f (cpanel, d, gg);
772 }
773 }
774
775
776 /*
777 * None of the following code makes any sense for the barchart...
778 */
779 if (!f) {
780 brush_boundaries_set (cpanel, &obin0, &obin1, &imin, &imax, d, gg);
781
782 for (ih = imin.x; ih <= imax.x; ih++) {
783 for (iv = imin.y; iv <= imax.y; iv++) {
784 for (m = 0; m < d->brush.binarray[ih][iv].nels; m++) {
785 /*
786 * j is the row number; k is the index of rows_in_plot.els[]
787 */
788 j = d->rows_in_plot.els[k = d->brush.binarray[ih][iv].els[m]];
789
790 switch (cpanel->br.point_targets) {
791 case br_candg: /*-- color and glyph --*/
792 changed = update_color_vectors (j, changed,
793 d->pts_under_brush.els, d, gg);
794 changed = update_glyph_vectors (j, changed,
795 d->pts_under_brush.els, d, gg);
796 break;
797 case br_color:
798 changed = update_color_vectors (j, changed,
799 d->pts_under_brush.els, d, gg);
800 break;
801 case br_glyph: /*-- glyph type and size --*/
802 changed = update_glyph_vectors (j, changed,
803 d->pts_under_brush.els, d, gg);
804 break;
805 case br_shadow: /*-- hidden --*/
806 changed = update_hidden_vectors (j, changed,
807 d->pts_under_brush.els, d, gg);
808 break;
809 case br_unshadow:
810 changed = bizarro_update_hidden_vectors (j, changed,
811 d->pts_under_brush.els,
812 d, gg);
813 break;
814 case br_off:
815 ;
816 break;
817 }
818
819 /*-- link by id --*/
820 if (!gg->linkby_cv && nd > 1)
821 symbol_link_by_id (false, j, d, gg);
822 /*-- --*/
823 }
824 }
825 }
826 }
827
828 obin0.x = d->brush.bin0.x;
829 obin0.y = d->brush.bin0.y;
830 obin1.x = d->brush.bin1.x;
831 obin1.y = d->brush.bin1.y;
832
833 return (changed);
834 }
835
836 /*----------------------------------------------------------------------*/
837 /* active_paint_points */
838 /*----------------------------------------------------------------------*/
839
840 /*
841 * Set pts_under_brush[j] to 1 if point j is inside the rectangular brush.
842 */
843 gboolean
active_paint_points(splotd * sp,GGobiData * d,ggobid * gg)844 active_paint_points (splotd * sp, GGobiData * d, ggobid * gg)
845 {
846 gint ih, iv, j, pt;
847 gboolean changed;
848 displayd *display = (displayd *) sp->displayptr;
849 cpaneld *cpanel = &display->cpanel;
850 gint (*f) (splotd * sp, GGobiData *, ggobid *) = NULL;
851 BrushTargetType ttype;
852
853 g_assert (d->pts_under_brush.nels == d->nrows);
854
855 if (GGOBI_IS_EXTENDED_SPLOT (sp)) {
856 f = GGOBI_EXTENDED_SPLOT_GET_CLASS (sp)->active_paint_points;
857 if (f) {
858 d->npts_under_brush = f (sp, d, gg);
859 }
860 }
861
862 if (!f) {
863
864 /* Zero out pts_under_brush[] before looping */
865 d->npts_under_brush = 0;
866 for (j = 0; j < d->nrows_in_plot; j++)
867 d->pts_under_brush.els[d->rows_in_plot.els[j]] = 0;
868
869 /*
870 * d->brush.binarray[][] only represents the
871 * cases in rows_in_plot.els[] so there's no need to test for that.
872 */
873
874 ttype = cpanel->br.point_targets;
875 for (ih = d->brush.bin0.x; ih <= d->brush.bin1.x; ih++) {
876 for (iv = d->brush.bin0.y; iv <= d->brush.bin1.y; iv++) {
877 for (j = 0; j < d->brush.binarray[ih][iv].nels; j++) {
878 pt = d->rows_in_plot.els[d->brush.binarray[ih][iv].els[j]];
879
880 /*
881 * Ignore hidden cases unless shadow or unshadow brushing.
882 */
883 if (d->hidden_now.els[pt] &&
884 (ttype != br_shadow && ttype != br_unshadow)) {
885 continue;
886 }
887 if (splot_plot_case (pt, d, sp, display, gg)) {
888 if (under_brush (pt, sp)) {
889 d->npts_under_brush++;
890 d->pts_under_brush.els[pt] = 1;
891 }
892 }
893 }
894 }
895 }
896 }
897
898 changed = false;
899
900 if (cpanel->br.brush_on_p) {
901 if (gg->linkby_cv) {
902 /*-- link by categorical variable --*/
903 changed = build_symbol_vectors_by_var (cpanel, d, gg);
904 }
905 else {
906 /*-- link by id --*/
907 changed = build_symbol_vectors (cpanel, d, gg);
908 }
909 }
910
911 return (changed);
912 }
913
914 /*----------------------------------------------------------------------*/
915 /* Edge brushing */
916 /*----------------------------------------------------------------------*/
917
918
919 static gboolean
xed_by_brush(gint k,displayd * display,ggobid * gg)920 xed_by_brush (gint k, displayd * display, ggobid * gg)
921 /*
922 * Determine whether edge k intersects the brush
923 */
924 {
925 GGobiData *d = display->d;
926 GGobiData *e = display->e;
927 splotd *sp = gg->current_splot;
928 gboolean intersect;
929 glong x1 = sp->brush_pos.x1;
930 glong y1 = sp->brush_pos.y1;
931 glong x2 = sp->brush_pos.x2;
932 glong y2 = sp->brush_pos.y2;
933 gint a, b;
934
935 endpointsd *endpoints;
936 endpoints = resolveEdgePoints (e, d);
937
938 if (!endpoints || !edge_endpoints_get (k, &a, &b, d, endpoints, e))
939 return false;
940
941 /*-- test for intersection with the vertical edge --*/
942 intersect = lines_intersect (x1 + (x2 - x1) / 2, y1, x1 + (x2 - x1) / 2, y2,
943 sp->screen[a].x, sp->screen[a].y,
944 sp->screen[b].x, sp->screen[b].y);
945 /*
946 intersect = isCrossed (x1 + (x2 - x1)/2, y1, x1 + (x2 - x1)/2, y2,
947 (gdouble) sp->screen[a].x, (gdouble) sp->screen[a].y,
948 (gdouble) sp->screen[b].x, (gdouble) sp->screen[b].y);
949 */
950
951 /*-- I wonder if Lee's method is truly faster --- it requires
952 doubles, which forces me to do a lot of casting. I should
953 figure out how to test it --*/
954 if (intersect != 1) {
955 intersect =
956 lines_intersect (x1, y1 + (y2 - y1) / 2, x2, y1 + (y2 - y1) / 2,
957 sp->screen[a].x, sp->screen[a].y, sp->screen[b].x,
958 sp->screen[b].y);
959 /*
960 intersect = isCrossed (x1, y1 + (y2 - y1)/2, x2, y1 + (y2 - y1)/2,
961 (gdouble) sp->screen[a].x, (gdouble) sp->screen[a].y,
962 (gdouble) sp->screen[b].x, (gdouble) sp->screen[b].y);
963 */
964 }
965
966 return (intersect == 1);
967 }
968
969 /*-- link by id --*/
970 static gboolean
build_edge_symbol_vectors(cpaneld * cpanel,GGobiData * e,ggobid * gg)971 build_edge_symbol_vectors (cpaneld * cpanel, GGobiData * e, ggobid * gg)
972 {
973 gint i;
974 gboolean changed = false;
975 gint nd = g_slist_length (gg->d);
976
977 /*
978 * I'm not doing any checking here to verify that the edges
979 * are displayed.
980 */
981 for (i = 0; i < e->edge.n; i++) {
982
983 switch (cpanel->br.edge_targets) {
984 case br_candg: /*-- color and glyph --*/
985 changed = update_color_vectors (i, changed,
986 e->edge.xed_by_brush.els, e, gg);
987 changed = update_glyph_vectors (i, changed,
988 e->edge.xed_by_brush.els, e, gg);
989 break;
990 case br_color: /*-- color --*/
991 changed = update_color_vectors (i, changed,
992 e->edge.xed_by_brush.els, e, gg);
993 break;
994 case br_glyph: /*-- line width and line type --*/
995 changed = update_glyph_vectors (i, changed,
996 e->edge.xed_by_brush.els, e, gg);
997 break;
998 case br_shadow: /*-- hidden --*/
999 changed = update_hidden_vectors (i, changed,
1000 e->edge.xed_by_brush.els, e, gg);
1001 break;
1002 case br_unshadow:
1003 changed = bizarro_update_hidden_vectors (i, changed,
1004 e->edge.xed_by_brush.els, e,
1005 gg);
1006 break;
1007 case br_off:
1008 ;
1009 break;
1010 }
1011
1012 /*-- link by id --*/
1013 if (!gg->linkby_cv && nd > 1)
1014 symbol_link_by_id (false, i, e, gg);
1015 /*-- --*/
1016 }
1017
1018 return (changed);
1019 }
1020
1021 static gboolean
active_paint_edges(splotd * sp,GGobiData * e,ggobid * gg)1022 active_paint_edges (splotd * sp, GGobiData * e, ggobid * gg)
1023 {
1024 gint k;
1025 gboolean changed;
1026 displayd *display = sp->displayptr;
1027 cpaneld *cpanel = &display->cpanel;
1028
1029 g_assert (e->edge.xed_by_brush.nels == e->edge.n);
1030
1031 /* Zero out xed_by_brush[] before looping */
1032 e->edge.nxed_by_brush = 0;
1033 for (k = 0; k < e->edge.n; k++)
1034 e->edge.xed_by_brush.els[k] = false;
1035
1036 for (k = 0; k < e->edge.n; k++) {
1037 if (xed_by_brush (k, display, gg)) {
1038 e->edge.nxed_by_brush++;
1039 e->edge.xed_by_brush.els[k] = true;
1040 }
1041 }
1042
1043 changed = false;
1044 if (cpanel->br.brush_on_p) {
1045 if (gg->linkby_cv) {
1046 /*-- link by categorical variable: presently unavailable --*/
1047 /*changed = build_symbol_vectors_by_var (cpanel, e, gg);*/
1048 /* Simply brush without linking */
1049 changed = build_edge_symbol_vectors (cpanel, e, gg);
1050 }
1051 else {
1052 /*-- link by id --*/
1053 changed = build_edge_symbol_vectors (cpanel, e, gg);
1054 }
1055 }
1056
1057 return (changed);
1058 }
1059