-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathplot_window.cpp
More file actions
executable file
·2860 lines (2503 loc) · 101 KB
/
plot_window.cpp
File metadata and controls
executable file
·2860 lines (2503 loc) · 101 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// viewpoints - interactive linked scatterplots and more.
// copyright 2005 Creon Levit and Paul Gazis, all rights reserved.
//***************************************************************************
// File name: Plot_window.cpp
//
// Class definitions:
// Plot_Window -- Plot window
//
// Classes referenced:
// control_panel_window -- Control panel window
//
// Required packages
// FLTK 1.1.6 -- Fast Light Toolkit graphics package
// FLEWS 0.3 -- Extensions to FLTK
// OGLEXP 1.2.2 -- Access to OpenGL extension under Windows
// GSL 1.6 -- Gnu Scientific Library package for Windows
// Blitz++ 0.9 -- Various math routines
//
// Compiler directives:
// May require D__WIN32__ for the C++ compiler
//
// Purpose: Source code for <Plot_Window.h>
//
// Author: Creon Levit 2005-2006
// Modified: Nathan Schmidt 01-SEP-2008
// Modified: P. R. Gazis 24-MAR-2009
//***************************************************************************
// Include the necessary include libraries
#include "include_libraries_vp.h"
// Include globals
#include "global_definitions_vp.h"
// Include associated headers and source code
#include "data_file_manager.h"
#include "plot_window.h"
#include "control_panel_window.h"
#include "sprite_textures.h"
#include "brush.h"
#include "column_info.h"
// experimental
#define ALPHA_TEXTURE
#define CHECK_GL_ERROR(msg) \
{ \
GLenum TMerrCode = glGetError(); \
if (TMerrCode != GL_NO_ERROR) { \
printf("%s: %s %s at %s:%d\n", \
__FUNCTION__, \
gluErrorString(TMerrCode), \
(msg), __FILE__, __LINE__); \
abort(); \
} \
}
// initialize static data members for class Plot_Window::
// Initial number of plot windows
int Plot_Window::count = 0;
// Initial fraction of the window to be used for showing (normalized) data
float const Plot_Window::initial_pscale = 0.8;
//GLfloat Plot_Window::texenvcolor[ 4] = { 1, 1, 1, 1};
// 2D array that holds indices of vertices for each brush
blitz::Array<unsigned int,2> Plot_Window::indices_selected(NBRUSHES,1);
GLuint Plot_Window::spriteTextureID[NSYMBOLS];
GLubyte* Plot_Window::spriteData[NSYMBOLS];
int Plot_Window::sprites_initialized = 0;
void *Plot_Window::global_GLContext = NULL;
int Plot_Window::indexVBOsinitialized = 0;
int Plot_Window::indexVBOsfilled = 0;
#define BUFFER_OFFSET(vbo_offset) ((char *)NULL + (vbo_offset))
// Declarations for global methods defined and used by class Plot_Window.
// NOTE: Is it a good idea to do this here rather than global_definitions.h?
void circular_shift(
blitz::Array<float,1> dst, blitz::Array<float,1> src, const int shift);
void moving_average(
blitz::Array<float,1> a, const blitz::Array<int,1> indices,
const int half_width);
void cummulative_conditional(
blitz::Array<float,1> a, const blitz::Array<int,1> indices,
const int half_width);
void fluctuation(
blitz::Array<float,1> a, const blitz::Array<int,1> indices,
const int half_width);
double nicenum( const double xx, const double round);
//***************************************************************************
// Plot_Window::Plot_Window() -- Default constructor, used only in
// association with serialization!
Plot_Window::Plot_Window() : Fl_Gl_Window( 10, 10),
index( 0),
x_save( 0), y_save( 0), w_save( 0), h_save( 0),
do_reset_view_with_show( 0)
{}
//***************************************************************************
// Plot_Window::Plot_Window( w, h) -- Constructor. Increment count of plot
// windows, resize arrays, and set mode. This constructor sets a flag to
// tell the package to show the windows as part on a RESET_VIEW operation.
Plot_Window::Plot_Window( int w, int h, int new_index) :
Fl_Gl_Window( w, h), do_reset_view_with_show( 0)
{
// Set flag, update count, and invoke initialzation method
count++;
index = new_index;
initialize();
}
//***************************************************************************
// Plot_Window::initialize() -- Initialize window parameters. Set flags,
// set set framebuffer modes, set up openGL context.
void Plot_Window::initialize()
{
do_reset_view_with_show = 0;
show_center_glyph = 0;
selection_changed = 0;
VBOinitialized = 0;
VBOfilled = false;
// Resize arrays
vertices.resize( npoints, 3);
nbins[0] = nbins[1] = nbins[2] = nbins_default;
counts.resize( nbins_max+2, 3);
counts_selected.resize( nbins_max+2, 3);
if( can_do(FL_RGB|FL_DOUBLE|FL_ALPHA|FL_DEPTH|FL_STENCIL)) {
mode( FL_RGB|FL_DOUBLE|FL_ALPHA|FL_DEPTH|FL_STENCIL);
cout << " mode: FL_RGB|FL_DOUBLE|FL_ALPHA|FL_DEPTH|FL_STENCIL" << endl;
}
else if( can_do(FL_RGB|FL_DOUBLE|FL_ALPHA|FL_DEPTH)) {
mode( FL_RGB|FL_DOUBLE|FL_ALPHA|FL_DEPTH);
cout << " mode: FL_RGB|FL_DOUBLE|FL_ALPHA|FL_DEPTH" << endl;
}
else if( can_do(FL_RGB8|FL_DOUBLE|FL_ALPHA|FL_DEPTH)) {
mode( FL_RGB8|FL_DOUBLE|FL_DEPTH|FL_ALPHA);
cout << " mode: FL_RGB8|FL_DOUBLE|FL_ALPHA|FL_DEPTH" << endl;
}
else if( can_do(FL_RGB|FL_DOUBLE|FL_ALPHA)) {
cout << "Warning: depth buffering not enabled" << endl;
mode( FL_RGB|FL_DOUBLE|FL_ALPHA);
cout << " mode: FL_RGB|FL_DOUBLE|FL_ALPHA" << endl;
}
else if( can_do(FL_RGB8|FL_DOUBLE|FL_ALPHA)) {
cout << "Warning: depth buffering not enabled" << endl;
mode( FL_RGB8|FL_DOUBLE|FL_ALPHA);
cout << " mode: FL_RGB8|FL_DOUBLE|FL_ALPHA" << endl;
}
else {
cerr << "Error: can not allocate double buffered RGBA window for plot window " << index << endl;
exit (-1);
}
indexVBOsinitialized=0;
sprites_initialized=0;
}
//***************************************************************************
// Plot_Window::make_state() -- Examine widgets to generate and save state
// parameters. Used only by the serialize method.
void Plot_Window::make_state()
{
x_save = x();
y_save = y();
w_save = w();
h_save = h();
}
//***************************************************************************
// Plot_Window::copy_state( &pw) -- Copy state parameters from another
// object that has been created by serialization.
void Plot_Window::copy_state( Plot_Window* pw)
{
index = pw->index;
x_save = pw->x_save;
y_save = pw->y_save;
w_save = pw->w_save;
h_save = pw->h_save;
}
//***************************************************************************
// Plot_Window::load_state() -- Load state parameters into widgets.
// WARNING: There is no proetction against bad state parameters or the
// possibility that this might be a default object without widgets.
void Plot_Window::load_state()
{
// For some reason this doesn't work, so use position and size instead.
// x( x_save);
// y( y_save);
// w( w_save);
// h( h_save);
position( x_save, y_save);
size( w_save, h_save);
}
//***************************************************************************
// Plot_Window::change_axes() -- Change axes for a plot to new axes which are
// (probably) not duplicates.
void Plot_Window::change_axes( int nchange)
{
nchange = 0;
// Loop: Examine control panel tabs and increment axis counts only for plots
// with x or y axis unlocked. This is not ideal.
for( int i=0; i<nplots; i++) {
if( !cps[i]->lock_axis1_button->value() || !cps[i]->lock_axis2_button->value())
nchange++;
}
// cout << "for window " << index << " nchange=" << nchange << endl;
// Get variable indices for this panel, then change variable indices for
// axes that are not locked. MCL observes that this code seems a little
// verbose
int i=cp->varindex1->value();
int j=cp->varindex2->value();
// cout << " (i,j) before = (" << i << "," << j << ")" << endl;
if (!cp->lock_axis1_button->value() && !cp->lock_axis2_button->value()) {
for( int k=0; k<nchange; k++) upper_triangle_incr( i, j, nvars);
cp->varindex1->value(i);
cp->varindex2->value(j);
}
else if (!cp->lock_axis1_button->value()) {
for( int k=0; k<nchange; k++) {
i = (i+1)%nvars;
cp->varindex1->value(i);
}
}
else if (!cp->lock_axis2_button->value()) {
for( int k=0; k<nchange; k++) {
j = (j+1)%nvars;
cp->varindex2->value(j);
}
}
// Extract, renormalize, and draw variables
cp->extract_and_redraw();
}
//***************************************************************************
// Plot_Window::update_linked_transforms() -- Use current plot's scale and
// offset to update all the others that show (any of) the same axes (using
// the same normalization).
void Plot_Window::update_linked_transforms()
{
if( !link_all_axes_button->value()) return;
// Get this plot's axis indices and normalization styles
int axis1=cp->varindex1->value();
int style1 = cp->x_normalization_style->value();
int axis2=cp->varindex2->value();
int style2 = cp->y_normalization_style->value();
// Loop: Find other plot windows that have any of the same axis indices
// active and the same normalization style, update the appropriate
// translation and scale values for them.
for( int i=0; i<nplots; i++) {
Plot_Window *p = pws[i];
// Don't need to update ourself
if( p == this) continue;
// Finally, figure out what me may want to change and how
if( p->cp->varindex1->value() == axis1 &&
p->cp->x_normalization_style->value() == style1) {
p->xscale = xscale;
p->xcenter = xcenter;
p->needs_redraw = 1;
}
else if( p->cp->varindex1->value() == axis2 &&
p->cp->x_normalization_style->value() == style2) {
p->xscale = yscale;
p->xcenter = ycenter;
p->needs_redraw = 1;
}
if( p->cp->varindex2->value() == axis1 &&
p->cp->y_normalization_style->value() == style1) {
p->yscale = xscale;
p->ycenter = xcenter;
p->needs_redraw = 1;
}
else if( p->cp->varindex2->value() == axis2 &&
p->cp->y_normalization_style->value() == style2) {
p->yscale = yscale;
p->ycenter = ycenter;
p->needs_redraw = 1;
}
// This is needed to make sure the scale marks on the axis are
// updated
p->screen_to_world(-1, -1, p->wmin[0], p->wmin[1]);
p->screen_to_world(+1, +1, p->wmax[0], p->wmax[1]);
}
}
//***************************************************************************
// Plot_Window::handle( event) -- Main event handler.
int Plot_Window::handle( int event)
{
// Current plot window (button pushes, mouse drags, etc) must get redrawn
// before others so that selections get colored correctly. Ugh.
switch( event) {
active_plot = index;
// Mouse button push
case FL_PUSH:
DEBUG(cout << "FL_PUSH at " << xprev << ", " << yprev << endl);
// Show the control panel associated with this plot window.
cpt->value(cps[this->index]);
xprev = Fl::event_x();
yprev = Fl::event_y();
// Center of double clicks
if( Fl::event_clicks() > 0) {
center_on_click( xprev, yprev);
return 1;
}
// middle button pushed => start zoom
if( (Fl::event_state() == FL_BUTTON2) ||
(Fl::event_state() == (FL_BUTTON1 | FL_CTRL))) {
// XXX wish this worked
#if 0
xzoomcenter = (float)xprev;
xzoomcenter = + (2.0*(xzoomcenter/(float)w()) -1.0) ; // window -> [-1,1]
yzoomcenter = (float)yprev;
yzoomcenter = - (2.0*(yzoomcenter/(float)h()) -1.0) ; // window -> [-1,1]
#endif
}
// right button pushed => start translating
else if( Fl::event_state(FL_BUTTON3) ||
(Fl::event_state() == (FL_BUTTON1 | FL_ALT)) ) {
show_center_glyph = 1;
needs_redraw = 1;
}
// left button pushed => start new selection, or start translating
// the old selection
else if( Fl::event_state() == FL_BUTTON1) {
// determine current and previous active plot windows
static Plot_Window *previous_plot, *current_plot = (Plot_Window *)NULL;
previous_plot = current_plot;
current_plot = this;
// determine current and previous active brushes
static Brush *previous_brush, *current_brush = (Brush *)NULL;
previous_brush = current_brush;
current_brush = dynamic_cast <Brush*> (brushes_tab->value());
assert (current_brush);
newly_selected(blitz::Range(0,npoints-1)) = 0;
// extend the selection under the following circumstances, otherwise replace.
if (current_brush->add_to_selection->value() || // current_brush->paint->value() || ???
current_brush != previous_brush ||
current_plot != previous_plot) {
previously_selected( blitz::Range(0,npoints-1)) = selected( blitz::Range( 0, npoints-1));
}
xdown = (float)xprev;
xdown = + (2.0*(xdown/(float)w()) -1.0) ; // window -> [-1,1]
xdown = xdown / xscale;
xdown = xdown + xcenter;
ydown = (float)yprev;
ydown = - (2.0*(ydown/(float)h()) -1.0) ; // window -> [-1,1]
ydown = ydown/yscale;
ydown = ydown + ycenter;
xtracked = xdown;
ytracked = ydown;
selection_changed = 1;
handle_selection ();
redraw_all_plots (index);
}
return 1;
// Mouse drag
case FL_DRAG:
DEBUG (printf ("FL_DRAG, event_state: %x\n", Fl::event_state()));
xcur = Fl::event_x();
ycur = Fl::event_y();
xdragged = xcur - xprev;
ydragged = -(ycur - yprev);
xprev = xcur;
yprev = ycur;
// drag with right mouse (or alt-left-mouse) => translate the view
if( Fl::event_state(FL_BUTTON3) ||
(Fl::event_state(FL_BUTTON1) && Fl::event_state(FL_ALT))) {
float xmove = xdragged*(1/xscale)*(2.0/w());
float ymove = ydragged*(1/yscale)*(2.0/h());
xcenter -= xmove;
ycenter -= ymove;
DEBUG ( cout << "translating (xcenter, ycenter) = (" << xcenter << ", " << ycenter << ")" << endl);
// redraw ();
show_center_glyph = 1;
needs_redraw = 1;
update_linked_transforms ();
}
// drag with middle-mouse (or c-left-mouse) => scale the view
else if( Fl::event_state(FL_BUTTON2) ||
(Fl::event_state(FL_BUTTON1) && Fl::event_state(FL_CTRL))) {
if( scale_histogram) {
xhscale *= 1 + xdragged*(2.0/w());
yhscale *= 1 + ydragged*(2.0/h());
}
else {
xscale *= 1 + xdragged*(2.0/w());
yscale *= 1 + ydragged*(2.0/h());
zscale *= 1 + 0.5 * (xdragged*(2.0/w()) + ydragged*(2.0/h())); // XXX hack.
DEBUG ( cout << "scaling (xscale, yscale) = (" << xscale << ", " << yscale << ")" << endl);
}
// redraw();
needs_redraw = 1;
update_linked_transforms ();
}
// drag with left mouse => continue selecting
else if( Fl::event_state(FL_BUTTON1)) {
// shift key down => move entire selection
if( Fl::event_key(FL_Shift_L) || Fl::event_key(FL_Shift_R)) {
xdown += xdragged*(1/xscale)*(2.0/w());
ydown += ydragged*(1/yscale)*(2.0/h());
xtracked += xdragged*(1/xscale)*(2.0/w());
ytracked += ydragged*(1/yscale)*(2.0/h());
}
// no shift key => move corner of selection
else {
xtracked = + (2.0*(xcur/(float)w()) -1.0) ; // window -> [-1,1]
xtracked = xtracked / xscale;
xtracked = xtracked + xcenter;
ytracked = - (2.0*(ycur/(float)h()) -1.0) ; // window -> [-1,1]
ytracked = ytracked/yscale;
ytracked = ytracked + ycenter;
}
// printf ("FL_DRAG & FL_BUTTON1, event_state: %x isdrag = %d xdragged=%f ydragged=%f\n", Fl::event_state(), isdrag, xdragged, ydragged);
if( ( fabs(xdragged)+fabs(ydragged))>0) {
selection_changed = 1;
if( defer_redraws_button->value()) {
redraw_one_plot ();
}
else {
handle_selection ();
redraw_all_plots (index);
}
}
}
screen_to_world (-1, -1, wmin[0], wmin[1]);
screen_to_world (+1, +1, wmax[0], wmax[1]);
return 1;
// Mouse button up
case FL_RELEASE:
DEBUG (cout << "FL_RELEASE at " << Fl::event_x() << ", " << Fl::event_y() << endl);
if( show_center_glyph) {
show_center_glyph = 0;
}
if (defer_redraws_button->value()) {
handle_selection();
redraw_all_plots(index);
}
else {
redraw_one_plot();
}
return 1;
// keypress, key is in Fl::event_key(), ascii in Fl::event_text(). Return
// 1 if you understand/use the keyboard event, 0 otherwise...
case FL_KEYDOWN:
DEBUG ( cout << "FL_KEYDOWN, event_key() = " << Fl::event_key() << endl);
// XXX should figure out how to share shortcuts between plot windows and
// control panels... later
switch( Fl::event_key()) {
// exit
case 'q':
if( expert_mode || make_confirmation_window( "Quit? Are you sure?") > 0)
exit( 0);
else
return 1;
// delete selected points from all future processing
case 'x':
case FL_Delete:
delete_selection( (Fl_Widget *) NULL);
return 1;
// Invert or restore (uninvert) selected and nonselected
case 'i':
invert_selection();
return 1;
// Clear selection
case 'c':
clear_selections( (Fl_Widget *) NULL);
return 1;
// Don't display / display deselected dots
case 'd':
toggle_display_deselected( (Fl_Widget *) NULL);
return 1;
// Reset view tranform for this plot
case 'r':
reset_view();
return 1;
// hold down 'h' and middle mouse drag to scale histogram bin height.
// there should really be a better way....
case 'h':
scale_histogram=1;
return 1;
// run a timing test, for performance tuning & profiling
case '9':
run_timing_test();
return 1;
// Center on click
case 'w':
center_on_click( Fl::event_x(), Fl::event_y());
return 1;
// Search for strings in x- or y-axis variable
case 'f':
{
char buf[1024];
char label[1024];
int col = Fl::event_shift()?cp->varindex1->value():cp->varindex2->value();
char cAxis[10];
strcpy( cAxis, "");
Fl::event_shift()?strcpy( cAxis, "x"):strcpy( cAxis, "y");
strcpy( buf, "");
sprintf(
label,
"Search for a string in the %s-axis variable, '%s'",
cAxis, Data_File_Manager::column_info[col].label.c_str());
make_find_window( label, buf);
if( buf[0]) {
select_on_string( buf, col);
}
return 1;
}
// toggle grid
case 'g':
cp->show_grid->value(1-cp->show_grid->value());
needs_redraw = 1;
return 1;
// Unrecognized key pressed: do nothing
default:
return 0;
}
// Keyboard key up
case FL_KEYUP:
DEBUG ( cout << "FL_KEYUP" << endl);
switch( Fl::event_key()) {
case 'h':
scale_histogram=0;
return 1;
default:
return 0;
}
// Shortcut, key is in Fl::event_key(), ascii in Fl::event_text(). Return
// 1 if you understand/use the shortcut event, 0 otherwise...
case FL_SHORTCUT:
return 0;
// Mouse wheel, zoom in both the x and y axes
case FL_MOUSEWHEEL:
if(1) {
float wheel_zoom_rate = 100.0;
float wheel_size_rate = 5.0;
float dy = Fl::event_dy();
float dx = Fl::event_dx();
if(dx) {
float newsz = cp->size->value();
newsz += dx / wheel_size_rate;
newsz = (cp->size->maximum()>(double)newsz)?newsz:cp->size->maximum();
newsz = (cp->size->minimum()>(double)newsz)?cp->size->minimum():newsz;
cp->size->value(newsz);
}
else {
xscale *= 1 - dy / wheel_zoom_rate;
yscale *= 1 - dy / wheel_zoom_rate;
}
needs_redraw = 1;
update_linked_transforms();
// make sure grids & axis ticks get updated since we've changed the view.
screen_to_world (-1, -1, wmin[0], wmin[1]);
screen_to_world (+1, +1, wmax[0], wmax[1]);
return 1;
}
// Default: Pass other events to the base class...
default:
return Fl_Gl_Window::handle( event);
}
}
//***************************************************************************
// Plot_Window::reset_selection_box() -- Reset the selection box.
void Plot_Window::reset_selection_box()
{
xdragged = ydragged = 0.0;
xzoomcenter = yzoomcenter = zzoomcenter = 0.0;
xdown = ydown = xtracked = ytracked = 0.0;
xprev = yprev = xcur = ycur = 0;
}
//***************************************************************************
// Plot_Window::redraw_one_plot() -- Invoke member functions to compute
// histograms and redraw one plot.
void Plot_Window::redraw_one_plot ()
{
DEBUG( cout << "in redraw_one_plot" << endl ) ;
compute_histograms();
redraw();
//Fl::flush();
needs_redraw = 0;
}
//***************************************************************************
// Plot_Window::reset_view() -- Reset pan, zoom, and angle.
void Plot_Window::reset_view()
{
// XXX this, and so much else, needs to be cleaned up, generalized to any
// and all axes. In particular, every possible c-style array should be
// replaced in viewpoints with a std::vector or a boost::array.
for (int i=0; i<3; i++) {
wmin[i] = amin[i];
wmax[i] = amax[i];
}
// Get third axis, if any
int axis2 = (long)(cp->varindex3->mvalue()->user_data());
// Regenerate axis scales
xscale = 2.0 / (wmax[0]-wmin[0]);
yscale = 2.0 / (wmax[1]-wmin[1]);
if (axis2 != nvars) zscale = 2.0 / (wmax[2]-wmin[2]);
else zscale = 1.0;
// Initially, datapoints only span 0.8 of the window dimensions, which
// allows room around the edges for labels, tickmarks, histograms....
xscale *= initial_pscale;
yscale *= initial_pscale;
zscale *= initial_pscale;
initial_scale = (xscale+yscale)/2.0;
magnification = 1.0;
// Get axis centers
xcenter = (wmin[0]+wmax[0]) / 2.0;
ycenter = (wmin[1]+wmax[1]) / 2.0;
if( axis2 != nvars) zcenter = (wmin[2]+wmax[2]) / 2.0;
else zcenter = 0.0;
// Get histogram scales (max height in window coords)
xhscale = 0.25;
yhscale = 0.25;
// Reset angle and stop any spin.
angle = 0.0;
cp->spin->value(0);
cp->rot_slider->value(0.0);
cp->dont_clear->value(0);
// Reset selection box and flag window as needing redraw
reset_selection_box ();
needs_redraw = 1;
// Make sure the window is visible and resizable. NOTE: For some reason,
// it is necessary to turn this off when a new plot window array is
// created or the windows will not be resizable!
if( do_reset_view_with_show & !visible()) {
this->show();
this->resizable( this);
}
}
//***************************************************************************
// Plot_Window::draw() -- Main draw method that calls others.
void Plot_Window::draw()
{
DEBUG (cout << "in draw: " << xcenter << " " << ycenter << " " << xscale << " " << yscale << " " << wmin[0] << " " << wmax[0] << endl);
// the valid() property can avoid reinitializing matrix for
// each redraw:
if( !valid()) {
valid(1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1, 1, -1, 1, 1000, -1000);
glViewport(0, 0, w(), h());
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glEnableClientState(GL_VERTEX_ARRAY);
// glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// glEnableClientState(GL_COLOR_ARRAY);
// this next idiom is necessary, per window, to map texture coordinate
// values to [0..1] for texturing.
// glMatrixMode(GL_TEXTURE);
// glLoadIdentity();
// glScalef( 1.0/(float)MAXPLOTS, 1.0/(float)MAXPLOTS, 1.0/(float)MAXPLOTS);
// glMatrixMode(GL_MODELVIEW);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef( xzoomcenter*xscale, yzoomcenter*yscale, zzoomcenter*zscale);
if( cp->spin->value())
angle += cp->rot_slider->value()/100.0;
else
angle = cp->rot_slider->value();
glRotatef(angle, 0.0, 1.0, 0.1);
glScalef (xscale, yscale, zscale);
//magnification = ((xscale+yscale)/2.0) / initial_scale;
magnification = sqrt(xscale*yscale) / initial_scale;
glTranslatef (-xcenter, -ycenter, -zcenter);
glTranslatef (-xzoomcenter, -yzoomcenter, -zzoomcenter);
if( cp->dont_clear->value() == 0) {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth (0.0);
glClearStencil (0);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
if( use_VBOs) {
if( !VBOinitialized) initialize_VBO();
if( !VBOfilled) fill_VBO();
if( !indexVBOsinitialized) initialize_indexVBOs();
if( !indexVBOsfilled) fill_indexVBOs();
}
draw_background ();
draw_data_points();
if( selection_changed) {
draw_selection_information();
}
draw_center_glyph();
draw_axes();
draw_grid();
draw_histograms ();
draw_resize_knob();
}
//***************************************************************************
// Plot_Window::center_on_click( x, y) -- Center on x, y on a mouse click.
void Plot_Window::center_on_click( int x, int y)
{
float xt;
float yt;
xt = + (2.0*(x/(float)w()) -1.0) ; // window -> [-1,1]
xt = xt / xscale;
xt = xt + xcenter;
yt = - (2.0*(y/(float)h()) -1.0) ; // window -> [-1,1]
yt = yt/yscale;
yt = yt + ycenter;
xcenter = xt;
ycenter = yt;
if(Fl::event_shift()) {
// zoom in, centered on click
xscale *= 1.5;
yscale *= 1.5;
} else if(Fl::event_key(FL_Alt_L)||Fl::event_key(FL_Alt_R)) {
// zoom out, centered on click
yscale /= 1.5;
xscale /= 1.5;
}
needs_redraw = 1;
update_linked_transforms ();
redraw_one_plot();
}
//***************************************************************************
// Plot_Window::draw_resize_knob() -- Draw visual target (knob) for resize.
void Plot_Window::draw_resize_knob()
{
#ifdef __APPLE__
glDisable( GL_DEPTH_TEST);
glEnable( GL_COLOR_LOGIC_OP);
glLogicOp( GL_XOR);
glBlendFunc( GL_ONE, GL_ZERO);
glColor4f( 0.5,0.5,0.5,1.0);
// Character buffers to contain strings for (fltk OpenGL) printing.
char buf1[] = "_|";
// Print a widget in lower-right to show where lower corner of the window is
gl_font( FL_HELVETICA_BOLD, 11);
glWindowPos2i( (w()-(int)gl_width(buf1)) - 1, 3);
gl_draw( (const char *) buf1);
glDisable( GL_COLOR_LOGIC_OP);
#endif // __APPLE__
}
//***************************************************************************
// Plot_Window::draw_background() -- If the background is anything besides
// black, draw it. Last. Why last? Because we always blend against a
// black background initially so that the overplotting comes out OK. Other
// backgrounds would interfere.
void Plot_Window::draw_background ()
{
if( cp->dont_clear->value() == 0) {
float bk = cp->Bkg->value();
if (bk > 0.0) {
glPushMatrix();
glLoadIdentity();
glEnable( GL_DEPTH_TEST);
glDepthFunc( GL_GEQUAL);
glColor4f(bk,bk,bk,1.0);
glBegin(GL_QUADS);
glVertex3f(-2,-2,-2);
glVertex3f(+2,-2,-2);
glVertex3f(+2,+2,-2);
glVertex3f(-2,+2,-2);
glEnd();
glPopMatrix();
}
}
}
//***************************************************************************
// Plot_Window::draw_grid() -- Draw a grid with lines in "nice" places
void Plot_Window::draw_grid()
{
if( cp->show_grid->value()) {
glDisable( GL_DEPTH_TEST);
glBlendFunc(GL_ONE, GL_ZERO);
gl_font( FL_HELVETICA_BOLD, 10);
const int nticks = 10;
const double extra=0.05;
const double range_x = wmax[0]-wmin[0], range_y = wmax[1]-wmin[1];
const double min_x = wmin[0]-extra*range_x, min_y = wmin[1]-extra*range_y;
const double max_x = wmax[0]+extra*range_x, max_y = wmax[1]+extra*range_y;
const double nice_range_x = nicenum(max_x-min_x, 0), nice_range_y = nicenum(max_y-min_y, 0);
const double d_x = nicenum(nice_range_x/(nticks-1),1), d_y = nicenum(nice_range_y/(nticks-1),1);
const double nicemin_x = floor(min_x/d_x)*d_x, nicemin_y = floor(min_y/d_y)*d_y;
const double nicemax_x = ceil(max_x/d_x)*d_x, nicemax_y = ceil(max_y/d_y)*d_y;
char format_str_x[20], format_str_y[20], temp[40], temp2[40];
const int nfrac = 8; // number of fractional digits to show. Was: max(-floor(log10(d)), 0);
// sprintf(format_str_x, "%%-%d.%dg", nfrac, nfrac);
sprintf(format_str_x, "%%.%dg", nfrac);
sprintf(format_str_y, "%%.%dg", nfrac);
DEBUG (printf("nicemin_x=%g nicemax_x=%g d_x=%g\n", nicemin_x, nicemax_x, d_x));
if (d_x == 0 || d_y == 0) {
cerr << " grid not drawn because of bad values " << endl;
return;
}
float bottom, top, left, right;
screen_to_world (-1, -1, left, bottom);
screen_to_world (+1, +1, right, top);
// Draw lines twice: first thick dark line, then thin light line. Good enough even w/o antialiasing.
int drawn=0;
for (float lum=0.66, width=1.5; width>0; drawn++, lum+=0.33, width-=1.0) {
glColor4f (lum, lum, lum, 1.0);
glLineWidth(width);
// lines of constant x, where x is nice
for (double x=nicemin_x+d_x; x<=nicemax_x-0.9999*d_x; x+=d_x) // was: for (x=nicemin; x<nicemax+.5*d; x+=d)
{
if (fabsf(x)<0.0001*d_x)
x=0;
glBegin( GL_LINES);
glVertex3f (x, nicemin_y+d_y, 0);
glVertex3f (x, nicemax_y-d_y, 0);
glEnd();
// perhaps we should carry around an up-to-date array of nice tick values for
// each axis object, and draw the ticks and labels in separate routines.
sprintf(temp, format_str_x, x);
DEBUG (printf("(%s)\n", temp));
float wx, wy;
screen_to_world (0, -1.15, wx, wy); // tweaked offsets
gl_draw( temp, x-gl_width(temp)/(w()*xscale), wy);
}
// lines of constant y, where y is nice
for (double y=nicemin_y+d_y; y<=nicemax_y-0.9999*d_y; y+=d_y) // was: for (y=nicemin; y<nicemax+.5*d; y+=d)
{
if (fabsf(y)<0.0001*d_y)
y=0;
glBegin( GL_LINES);
glVertex3f (nicemin_x+d_x, y, 0);
glVertex3f (nicemax_x-d_x, y, 0);
glEnd();
sprintf(temp2, format_str_y, y);
float wx, wy;
screen_to_world (+1.1, 0, wx, wy); // tweaked offsets
gl_draw( temp2, wx, y-0.5*gl_height()/((h()*yscale)));
}
}
}
}
//***************************************************************************
// Plot_Window::screen_to_world( xscreen, yscreen, xworld, yworld) -- Convert
// from screen to world co-ordinates?
void Plot_Window::screen_to_world(
float xscreen, float yscreen, float &xworld, float &yworld)
{
DEBUG (cout << "screen_to_world" << endl
<< " before xscreen: " << xscreen << " yscreen: " << yscreen
<< " xworld: " << xworld << " yworld: " << yworld << endl
<< " pscale: " << initial_pscale
<< " xscale: " << xscale << " xcenter: " << xcenter
<< " yscale: " << yscale << " ycenter: " << ycenter
<< endl) ;
xworld = ( xscreen*initial_pscale / xscale) + xcenter;
yworld = ( yscreen*initial_pscale / yscale) + ycenter;
DEBUG (cout << " after xscreen: " << xscreen << " yscreen: " << yscreen
<< " xworld: " << xworld << " yworld: " << yworld << endl);
}
//***************************************************************************
// Plot_Window::interval_to_strings( x1, x2, *buf1, *buf2) -- If this is
// ASCII-valued data, convert range values for this interval to strings.
void Plot_Window::interval_to_strings(
const int column, const float x1, const float x2, char *buf1, char *buf2)
{
// If this is a numerical axis, write numerical information. Otherwise
// identify and write the relevant strings.
if( Data_File_Manager::column_info[column].hasASCII == 0) {
sprintf( buf1, "%+.3g", x1);
sprintf( buf2, "%+.3g", x2);
}
else {
int ixmin = max( -1, (int) (floorf(x1-0.01/(float)npoints)));
int ixmax = max( -1, (int) (floorf(x2)));
// If the interval spans at least one integral value, look up and build
// string corresponding to each edge. Otherwise just write the same
// string twice.
if( ixmax > ixmin) {
const char *chrp1 =
Data_File_Manager::column_info[column].ascii_value(ixmin+1).c_str();
const char *chrp2 =
Data_File_Manager::column_info[column].ascii_value(ixmax).c_str();
sprintf( buf1, "%11s", chrp1);
sprintf( buf2, "%-11s", chrp2);
}
else {
sprintf( buf1, "%s", "");
sprintf( buf2, "%s", "");
}
}
}
//***************************************************************************
// Plot_Window::draw_axes() -- If requested, draw and label the axes
void Plot_Window::draw_axes()
{
// If requested draw axes
if( cp->show_axes->value() || cp->show_scale->value() ) {
glPushMatrix();
glLoadIdentity();