~hwkrus/f03gl/trunk

1 by Henk Krus
Initial Launchpad setup
1
! This program plots a function of two variables.  The function, called
2
! func_to_plot, is an external procedure at the end of the file.  
3
! This begins with the same module used in the modview example, followed by
4
! another module for plotting the function, called function_plotter.
5
! You might want to change default initial settings in modules modview and
6
! function_plotter.
7
8
! William F. Mitchell
9
! william.mitchell@nist.gov
10
! Mathematical and Computational Sciences Division
11
! National Institute of Standards and Technology
12
! August, 1999
13
14
!---------------------------------------------------------------------------
15
16
module view_modifier
17
18
! This module provides facilities to modify the view in an OpenGL window.
19
! The mouse buttons and keyboard arrow keys can be used to zoom, pan,
20
! rotate and change the scale.  A menu or submenu can be used to select which
21
! buttons perform which function and to reset the view to the initial settings.
22
! This is limited to one window.
23
24
! William F. Mitchell
25
! william.mitchell@nist.gov
26
! Mathematical and Computational Sciences Division
27
! National Institute of Standards and Technology
28
! April, 1998
29
30
! To use this module:
31
!
32
! 1) put a USE view_modifier statement in any program unit that calls a
33
!    procedure in this module
34
!
35
! 2) set the initial operation assignments, view and scale below the
36
!    "Initial configuration" comment below
37
!
38
! 3) call view_modifier_init after glutCreateWindow
39
!    This is a function that returns integer(kind=glcint) menuid.  The menuid
40
!    is the ID returned by glutCreateMenu.  You can either use the view_modifier
41
!    menu as your menu by calling glutAttachMenu immediately after
42
!    view_modifier_init, as in
43
!       menuid = view_modifier_init()
44
!       call glutAttachMenu(GLUT_RIGHT_BUTTON)
45
!    or by using the menuid to attach a submenu to your own menu, as in
46
!       call glutAddSubMenu("View Modifier",menuid)
47
!
48
! 4) in any callback functions that update the display, put
49
!       call reset_view
50
!    as the first executable statement
51
!
52
! Note that view_modifier_init sets the callback functions for glutMouseFunc,
53
! glutMotionFunc and glutSpecialFunc, so don't call these yourself
54
!
55
! The menu allows you to select what operation is attached to the left and
56
! middle mouse buttons and arrow keys, reset to the initial view, and quit.
57
! The right mouse button should be used for the menu.
58
59
use opengl_gl
60
use opengl_glu
61
use opengl_glut
62
implicit none
63
private
64
public :: view_modifier_init, reset_view
65
private :: ZOOM, PAN, ROTATE, SCALEX, SCALEY, SCALEZ, RESET, ABOVE, QUIT, &
66
           PI, &
67
           left_button_func, middle_button_func, arrow_key_func, &
68
           init_lookat, init_lookfrom, &
69
           init_xscale_factor, init_yscale_factor, init_zscale_factor, &
70
           angle, shift, xscale_factor, yscale_factor, zscale_factor, &
71
           moving_left, moving_middle, begin_left, begin_middle, &
72
           cart2sphere, sphere2cart, cart3D_plus_cart3D, cart3D_minus_cart3D, &
73
           reset_to_init, mouse, motion, arrows, &
74
           menu_handler, set_left_button, set_middle_button, set_arrow_keys
75
76
integer(kind=glcint), parameter :: ZOOM = 1, PAN = 2, ROTATE = 3, SCALEX = 4, &
77
                      SCALEY = 5, SCALEZ = 6
78
integer(kind=glcint), parameter :: RESET = 10, ABOVE = 11, QUIT = 12
79
real(kind=gldouble), parameter :: PI = 3.141592653589793_gldouble
80
81
type, private :: cart2D ! 2D cartesian coordinates
82
   real(kind=gldouble) :: x, y
83
end type cart2D
84
85
type, private :: cart3D ! 3D cartesian coordinates
86
   real(kind=gldouble) :: x, y, z
87
end type cart3D
88
89
type, private :: sphere3D ! 3D spherical coordinates
90
   real(kind=gldouble) :: theta, phi, rho
91
end type sphere3D
92
93
type(cart2D), save :: angle
94
type(cart3D), save :: shift
95
real(kind=gldouble), save :: xscale_factor, yscale_factor, zscale_factor
96
logical, save :: moving_left, moving_middle
97
type(cart2D), save :: begin_left, begin_middle
98
99
interface operator(+)
100
   module procedure cart3D_plus_cart3D
101
end interface
102
interface operator(-)
103
   module procedure cart3D_minus_cart3D
104
end interface
105
106
! ------- Initial configuration -------
107
108
! Set the initial operation performed by each button and the arrow keys.
109
! The operations are ZOOM, PAN, ROTATE, SCALEX, SCALEY, and SCALEZ
110
111
integer, save ::   left_button_func = ROTATE, &
112
                 middle_button_func = ZOOM, &
113
                     arrow_key_func = PAN
114
115
! Set the initial view as the point you are looking at, the point you are
116
! looking from, and the scale factors
117
118
type(cart3D), parameter :: &
119
     init_lookat = cart3D(0.5_gldouble, 0.5_gldouble, 0.0_gldouble), &
120
   init_lookfrom = cart3D(5.0_gldouble, 10.0_gldouble, 2.5_gldouble)
121
122
real(kind=gldouble), parameter :: &
123
   init_xscale_factor = 1.0_gldouble, &
124
   init_yscale_factor = 1.0_gldouble, &
125
   init_zscale_factor = 1.0_gldouble
126
127
! -------- end of Initial configuration ------
128
129
contains
130
131
!          -------------
132
subroutine reset_to_init
133
!          -------------
134
135
! This resets the view to the initial configuration
136
137
type(sphere3D) :: slookfrom
138
139
slookfrom = cart2sphere(init_lookfrom-init_lookat)
140
angle%x = -180.0_gldouble*slookfrom%theta/PI - 90.0_gldouble
141
angle%y = -180.0_gldouble*slookfrom%phi/PI
142
shift%x = 0.0_gldouble
143
shift%y = 0.0_gldouble
144
shift%z = -slookfrom%rho
145
xscale_factor = init_xscale_factor
146
yscale_factor = init_yscale_factor
147
zscale_factor = init_zscale_factor
148
149
call glutPostRedisplay
150
151
return
152
end subroutine reset_to_init
153
154
!          ---------------
155
subroutine view_from_above
156
!          ---------------
157
158
! This sets the view to be from straight above
159
160
type(sphere3D) :: slookfrom
161
162
slookfrom = cart2sphere(cart3D(0.0,0.0,1.0))
163
angle%x = -180.0_gldouble*slookfrom%theta/PI
164
angle%y = -180.0_gldouble*slookfrom%phi/PI
165
166
call glutPostRedisplay
167
168
return
169
end subroutine view_from_above
170
171
!          ----------
172
subroutine reset_view
173
!          ----------
174
175
! This routine resets the view to the current orientation and scale
176
177
call glMatrixMode(GL_MODELVIEW)
178
call glPopMatrix
179
call glPushMatrix
180
call glTranslated(shift%x, shift%y, shift%z)
181
call glRotated(angle%x, 0.0_gldouble, 0.0_gldouble, 1.0_gldouble)
182
call glRotated(angle%y, cos(PI*angle%x/180.0_gldouble), &
183
               -sin(PI*angle%x/180.0_gldouble), 0.0_gldouble)
184
call glTranslated(-init_lookat%x, -init_lookat%y, -init_lookat%z)
185
call glScaled(xscale_factor,yscale_factor,zscale_factor)
186
187
return
188
end subroutine reset_view
189
190
!          -----
191
subroutine mouse(button, state, x, y) bind(c,name="")
192
!          -----
193
integer(kind=glcint), value :: button, state, x, y
194
195
! This gets called when a mouse button changes
196
 
197
  if (button == GLUT_LEFT_BUTTON .and. state == GLUT_DOWN) then
198
    moving_left = .true.
199
    begin_left = cart2D(x,y)
200
  endif
201
  if (button == GLUT_LEFT_BUTTON .and. state == GLUT_UP) then
202
    moving_left = .false.
203
  endif
204
  if (button == GLUT_MIDDLE_BUTTON .and. state == GLUT_DOWN) then
205
    moving_middle = .true.
206
    begin_middle = cart2D(x,y)
207
  endif
208
  if (button == GLUT_MIDDLE_BUTTON .and. state == GLUT_UP) then
209
    moving_middle = .false.
210
  endif
211
end subroutine mouse
212
213
!          ------
214
subroutine motion(x, y) bind(c,name="")
215
!          ------
216
integer(kind=glcint), value :: x, y
217
218
! This gets called when the mouse moves
219
220
integer :: button_function
221
type(cart2D) :: begin
222
real(kind=gldouble) :: factor
223
224
! Determine and apply the button function
225
226
if (moving_left) then
227
   button_function = left_button_func
228
   begin = begin_left
229
else if(moving_middle) then
230
   button_function = middle_button_func
231
   begin = begin_middle
232
end if
233
234
select case(button_function)
235
case (ZOOM)
236
   if (y < begin%y) then
237
      factor = 1.0_gldouble/(1.0_gldouble + .002_gldouble*(begin%y-y))
238
   else if (y > begin%y) then
239
      factor = 1.0_gldouble + .002_gldouble*(y-begin%y)
240
   else
241
      factor = 1.0_gldouble
242
   end if
243
   shift%z = factor*shift%z
244
case (PAN)
245
   shift%x = shift%x + .01*(x - begin%x)
246
   shift%y = shift%y - .01*(y - begin%y)
247
case (ROTATE)
248
   angle%x = angle%x + (x - begin%x)
249
   angle%y = angle%y + (y - begin%y)
250
case (SCALEX)
251
   if (y < begin%y) then
252
      factor = 1.0_gldouble + .002_gldouble*(begin%y-y)
253
   else if (y > begin%y) then
254
      factor = 1.0_gldouble/(1.0_gldouble + .002_gldouble*(y-begin%y))
255
   else
256
      factor = 1.0_gldouble
257
   end if
258
   xscale_factor = xscale_factor * factor
259
case (SCALEY)
260
   if (y < begin%y) then
261
      factor = 1.0_gldouble + .002_gldouble*(begin%y-y)
262
   else if (y > begin%y) then
263
      factor = 1.0_gldouble/(1.0_gldouble + .002_gldouble*(y-begin%y))
264
   else
265
      factor = 1.0_gldouble
266
   end if
267
   yscale_factor = yscale_factor * factor
268
case (SCALEZ)
269
   if (y < begin%y) then
270
      factor = 1.0_gldouble + .002_gldouble*(begin%y-y)
271
   else if (y > begin%y) then
272
      factor = 1.0_gldouble/(1.0_gldouble + .002_gldouble*(y-begin%y))
273
   else
274
      factor = 1.0_gldouble
275
   end if
276
   zscale_factor = zscale_factor * factor
277
end select
278
279
! update private variables and redisplay
280
281
if (moving_left) then
282
   begin_left = cart2D(x,y)
283
else if(moving_middle) then
284
   begin_middle = cart2D(x,y)
285
endif
286
287
if (moving_left .or. moving_middle) then
288
   call glutPostRedisplay
289
endif
290
291
return
292
end subroutine motion
293
294
!          ------
295
subroutine arrows(key, x, y) bind(c,name="")
296
!          ------
297
integer(glcint), value :: key, x, y
298
299
! This routine handles the arrow key operations
300
301
real(kind=gldouble) :: factor
302
303
select case(arrow_key_func)
304
case(ZOOM)
305
   select case(key)
306
   case(GLUT_KEY_DOWN)
307
      factor = 1.0_gldouble + .02_gldouble
308
   case(GLUT_KEY_UP)
309
      factor = 1.0_gldouble/(1.0_gldouble + .02_gldouble)
310
   case default
311
      factor = 1.0_gldouble
312
   end select
313
   shift%z = factor*shift%z
314
case(PAN)
315
   select case(key)
316
   case(GLUT_KEY_LEFT)
317
      shift%x = shift%x - .02
318
   case(GLUT_KEY_RIGHT)
319
      shift%x = shift%x + .02
320
   case(GLUT_KEY_DOWN)
321
      shift%y = shift%y - .02
322
   case(GLUT_KEY_UP)
323
      shift%y = shift%y + .02
324
   end select
325
case(ROTATE)
326
   select case(key)
327
   case(GLUT_KEY_LEFT)
328
      angle%x = angle%x - 1.0_gldouble
329
   case(GLUT_KEY_RIGHT)
330
      angle%x = angle%x + 1.0_gldouble
331
   case(GLUT_KEY_DOWN)
332
      angle%y = angle%y + 1.0_gldouble
333
   case(GLUT_KEY_UP)
334
      angle%y = angle%y - 1.0_gldouble
335
   end select
336
case(SCALEX)
337
   select case(key)
338
   case(GLUT_KEY_DOWN)
339
      factor = 1.0_gldouble/(1.0_gldouble + .02_gldouble)
340
   case(GLUT_KEY_UP)
341
      factor = 1.0_gldouble + .02_gldouble
342
   case default
343
      factor = 1.0_gldouble
344
   end select
345
   xscale_factor = xscale_factor * factor
346
case(SCALEY)
347
   select case(key)
348
   case(GLUT_KEY_DOWN)
349
      factor = 1.0_gldouble/(1.0_gldouble + .02_gldouble)
350
   case(GLUT_KEY_UP)
351
      factor = 1.0_gldouble + .02_gldouble
352
   case default
353
      factor = 1.0_gldouble
354
   end select
355
   yscale_factor = yscale_factor * factor
356
case(SCALEZ)
357
   select case(key)
358
   case(GLUT_KEY_DOWN)
359
      factor = 1.0_gldouble/(1.0_gldouble + .02_gldouble)
360
   case(GLUT_KEY_UP)
361
      factor = 1.0_gldouble + .02_gldouble
362
   case default
363
      factor = 1.0_gldouble
364
   end select
365
   zscale_factor = zscale_factor * factor
366
367
end select
368
   
369
call glutPostRedisplay
370
371
return
372
end subroutine arrows
373
374
!          ------------
375
subroutine menu_handler(value) bind(c,name="")
376
!          ------------
377
integer(kind=glcint), value :: value
378
379
! This routine handles the first level entries in the menu
380
381
select case(value)
382
383
case(RESET)
384
   call reset_to_init
385
case(ABOVE)
386
   call view_from_above
387
case(QUIT)
388
   stop
389
390
end select
391
392
return
393
end subroutine menu_handler
394
395
!          ---------------
396
subroutine set_left_button(value) bind(c,name="")
397
!          ---------------
398
integer(kind=glcint), value :: value
399
400
! This routine sets the function of the left button as given by menu selection
401
402
left_button_func = value
403
404
return
405
end subroutine set_left_button
406
407
!          -----------------
408
subroutine set_middle_button(value)  bind(c,name="")
409
!          -----------------
410
integer(kind=glcint), value :: value
411
412
! This routine sets the function of the middle button as given by menu selection
413
414
middle_button_func = value
415
416
return
417
end subroutine set_middle_button
418
419
!          --------------
420
subroutine set_arrow_keys(value)  bind(c,name="")
421
!          --------------
422
integer(kind=glcint), value :: value
423
424
! This routine sets the function of the arrow keys as given by menu selection
425
426
arrow_key_func = value
427
428
return
429
end subroutine set_arrow_keys
430
431
!        ------------------
432
function view_modifier_init() result(menuid)
433
!        ------------------
434
integer(kind=glcint) :: menuid
435
436
! This initializes the view modifier variables and sets initial view.
437
! It should be called immediately after glutCreateWindow
438
439
integer(kind=glcint) :: button_left, button_middle, arrow_keys
440
441
! set the callback functions
442
443
call glutMouseFunc(mouse)
444
call glutMotionFunc(motion)
445
call glutSpecialFunc(arrows)
446
447
! create the menu
448
449
button_left = glutCreateMenu(set_left_button)
450
call glutAddMenuEntry("rotate"//char(0),ROTATE)
451
call glutAddMenuEntry("zoom"//char(0),ZOOM)
452
call glutAddMenuEntry("pan"//char(0),PAN)
453
call glutAddMenuEntry("scale x"//char(0),SCALEX)
454
call glutAddMenuEntry("scale y"//char(0),SCALEY)
455
call glutAddMenuEntry("scale z"//char(0), SCALEZ)
456
button_middle = glutCreateMenu(set_middle_button)
457
call glutAddMenuEntry("rotate"//char(0),ROTATE)
458
call glutAddMenuEntry("zoom"//char(0),ZOOM)
459
call glutAddMenuEntry("pan"//char(0),PAN)
460
call glutAddMenuEntry("scale x"//char(0),SCALEX)
461
call glutAddMenuEntry("scale y"//char(0),SCALEY)
462
call glutAddMenuEntry("scale z"//char(0), SCALEZ)
463
arrow_keys = glutCreateMenu(set_arrow_keys)
464
call glutAddMenuEntry("rotate"//char(0),ROTATE)
465
call glutAddMenuEntry("zoom"//char(0),ZOOM)
466
call glutAddMenuEntry("pan"//char(0),PAN)
467
call glutAddMenuEntry("scale x"//char(0),SCALEX)
468
call glutAddMenuEntry("scale y"//char(0),SCALEY)
469
call glutAddMenuEntry("scale z"//char(0), SCALEZ)
470
menuid = glutCreateMenu(menu_handler)
471
call glutAddSubMenu("left mouse button"//char(0),button_left)
472
call glutAddSubMenu("middle mouse button"//char(0),button_middle)
473
call glutAddSubMenu("arrow keys"//char(0),arrow_keys)
474
call glutAddMenuEntry("reset to initial view"//char(0),RESET)
475
call glutAddMenuEntry("view from above"//char(0),ABOVE)
476
call glutAddMenuEntry("quit"//char(0),QUIT)
477
478
! set the perspective
479
480
call glMatrixMode(GL_PROJECTION)
481
call gluPerspective(10.0_gldouble, 1.0_gldouble, 0.1_gldouble, 200.0_gldouble)
482
483
! set the initial view
484
485
call glMatrixMode(GL_MODELVIEW)
486
call glPushMatrix
487
call reset_to_init
488
489
return
490
end function view_modifier_init
491
492
!        -----------
493
function sphere2cart(spoint) result(cpoint)
494
!        -----------
495
type(sphere3D), intent(in) :: spoint
496
type(cart3D) :: cpoint
497
498
! This converts a 3D point from spherical to cartesean coordinates
499
500
real(kind=gldouble) :: t,p,r
501
502
t=spoint%theta
503
p=spoint%phi
504
r=spoint%rho
505
506
cpoint%x = r*cos(t)*sin(p)
507
cpoint%y = r*sin(t)*sin(p)
508
cpoint%z = r*cos(p)
509
510
return
511
end function sphere2cart
512
513
!        -----------
514
function cart2sphere(cpoint) result(spoint)
515
!        -----------
516
type(cart3D), intent(in) :: cpoint
517
type(sphere3D) :: spoint
518
519
! This converts a 3D point from cartesean to spherical coordinates
520
521
real(kind=gldouble) :: x,y,z
522
523
x=cpoint%x
524
y=cpoint%y
525
z=cpoint%z
526
527
spoint%rho = sqrt(x*x+y*y+z*z)
528
if (x==0.0_gldouble .and. y==0.0_gldouble) then
529
   spoint%theta = 0.0_gldouble
530
else
531
   spoint%theta = atan2(y,x)
532
end if
533
if (spoint%rho == 0.0_gldouble) then
534
   spoint%phi = 0.0_gldouble
535
else
536
   spoint%phi = acos(z/spoint%rho)
537
endif
538
539
return
540
end function cart2sphere
541
542
!        ------------------
543
function cart3D_plus_cart3D(cart1,cart2) result(cart3)
544
!        ------------------
545
type(cart3D), intent(in) :: cart1, cart2
546
type(cart3D) :: cart3
547
548
! Compute the sum of two 3D cartesean points
549
550
cart3%x = cart1%x + cart2%x
551
cart3%y = cart1%y + cart2%y
552
cart3%z = cart1%z + cart2%z
553
554
return
555
end function cart3D_plus_cart3D
556
557
!        -------------------
558
function cart3D_minus_cart3D(cart1,cart2) result(cart3)
559
!        -------------------
560
type(cart3D), intent(in) :: cart1, cart2
561
type(cart3D) :: cart3
562
563
! Compute the difference of two 3D cartesean points
564
565
cart3%x = cart1%x - cart2%x
566
cart3%y = cart1%y - cart2%y
567
cart3%z = cart1%z - cart2%z
568
569
return
570
end function cart3D_minus_cart3D
571
572
end module view_modifier
573
574
!---------------------------------------------------------------------------
575
576
module function_plotter
577
use opengl_gl
578
use opengl_glut
579
use view_modifier
580
implicit none
581
private
582
public :: display, draw_func, menu_handler2, make_menu
583
584
! symbolic constants
585
586
integer, parameter :: surfgrid_toggle = 1, &
587
                      surfsolid_toggle = 2, &
588
                      contour_toggle = 3, &
589
                      quit_selected = 4
590
591
integer, parameter :: set_nx = 1, &
592
                      set_ny = 2, &
593
                      set_ncontour = 3, &
594
                      set_contour_val = 4, &
595
                      set_xrange = 5, &
596
                      set_yrange = 6, &
597
                      reset_params = 7
598
599
integer, parameter :: black_contour = 1, &
600
                      rainbow_contour = 2
601
602
integer, parameter :: white_surface = 1, &
603
                      red_surface = 2, &
604
                      rainbow_surface = 3
605
606
! Default initial settings
607
608
integer, parameter :: init_ngridx = 40, &
609
                      init_ngridy = 40, &
610
                      init_num_contour = 20, &
611
                      init_contour_color = black_contour, &
612
                      init_surface_color = rainbow_surface
613
614
real(GLDOUBLE), parameter :: init_minx = 0.0_GLDOUBLE, &
615
                             init_maxx = 1.0_GLDOUBLE, &
616
                             init_miny = 0.0_GLDOUBLE, &
617
                             init_maxy = 1.0_GLDOUBLE
618
619
logical, parameter :: init_draw_surface_grid = .false., &
620
                      init_draw_surface_solid = .true., &
621
                      init_draw_contour = .true.
622
623
! Current settings
624
625
integer :: ngridx = init_ngridx, &
626
           ngridy = init_ngridy, &
627
           num_contour = init_num_contour, &
628
           contour_color = init_contour_color, &
629
           surface_color = init_surface_color
630
631
real(GLDOUBLE) :: minx = init_minx, &
632
                  maxx = init_maxx, &
633
                  miny = init_miny, &
634
                  maxy = init_maxy, &
635
                  minz = 0.0_GLDOUBLE, &
636
                  maxz = 0.0_GLDOUBLE
637
638
logical :: draw_surface_grid = init_draw_surface_grid, &
639
           draw_surface_solid = init_draw_surface_solid, &
640
           draw_contour = init_draw_contour, &
641
           contour_values_given = .false.
642
643
real(GLDOUBLE), allocatable :: actual_contours(:)
644
645
contains
646
647
subroutine display() bind(c)
648
649
! This gets called when the display needs to be redrawn
650
651
call reset_view
652
653
call glClear(ior(GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT))
654
call glCallList(1)
655
call glutSwapBuffers
656
657
return
658
end subroutine display
659
660
subroutine draw_func
661
real(GLDOUBLE) :: gridx(0:ngridx),gridy(0:ngridy),zval(0:ngridy,2)
662
integer :: i,j,k,cont
663
real(GLDOUBLE) :: x1,x2,x3,xt,y1,y2,y3,yt,z1,z2,z3,zt
664
real(GLDOUBLE) :: frac,xcross1,xcross2,ycross1,ycross2
665
real(GLDOUBLE) :: contour_value(num_contour)
666
real(GLFLOAT) :: color(4), normal(3), &
667
                 red(4) = (/1.0,0.0,0.0,1.0/), &
668
                 black(4) = (/0.0,0.0,0.0,1.0/), &
669
                 white(4) = (/1.0,1.0,1.0,1.0/)
670
real(GLDOUBLE), external :: func_to_plot
671
672
! prepare to make a new display list
673
674
call reset_view
675
call glDeleteLists(1_gluint, 1_glsizei)
676
call glNewList(1_gluint, gl_compile_and_execute)
677
678
! set the grid points
679
680
gridx = (/ ((minx + i*(maxx-minx)/ngridx),i=0,ngridx) /)
681
gridy = (/ ((miny + i*(maxy-miny)/ngridy),i=0,ngridy) /)
682
683
! if this is the first call and either rainbow coloring of a solid surface
684
! or contours are being drawn, minz and maxz need to be set
685
686
if (minz == 0.0_GLDOUBLE .and. maxz == 0.0_GLDOUBLE .and. &
687
    ( (draw_surface_solid .and. surface_color == rainbow_surface) .or. &
688
      draw_contour ) ) then
689
   do i=0,ngridx
690
      do j=0,ngridy
691
         z1 = func_to_plot(gridx(i),gridy(j))
692
         minz = min(z1,minz)
693
         maxz = max(z1,maxz)
694
      end do
695
   end do
696
endif
697
698
! draw the solid surface
699
700
if (draw_surface_solid) then
701
702
   call glPolygonMode(gl_front_and_back, gl_fill)
703
   call glBegin(gl_triangles)
704
705
! set the color for a red or white surface.  For white, set the lighting
706
! such that there is uniform brightness.
707
708
   if (surface_color == red_surface) then
709
      call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,red)
710
   elseif (surface_color == white_surface) then
711
      call glDisable(gl_light0)
712
      call glLightModelfv(gl_light_model_ambient, (/1.,1.,1.,1./))
713
      call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,white)
714
   endif
715
716
! compute the function values for the first line of points
717
718
   do j=0,ngridy
719
      zval(j,2) = func_to_plot(gridx(0),gridy(j))
720
   end do
721
722
! for each x grid interval...
723
724
   do i=1,ngridx
725
726
! copy left side function values from the right side of the previous interval
727
728
      zval(:,1) = zval(:,2)
729
730
! compute the function values for the right side of the interval
731
732
      do j=0,ngridy
733
         zval(j,2) = func_to_plot(gridx(i),gridy(j))
734
      end do
735
736
      minz = min(minz,minval(zval))
737
      maxz = max(maxz,maxval(zval))
738
739
! for each y grid interval ...
740
741
      do j=1,ngridy
742
743
! add two triangles to the display list
744
! Before each triangle, set the normal.  Before each vertex, set the
745
! color if we're coloring by height
746
747
         normal = normcrossprod((/gridx(i-1),gridx(i),gridx(i)/), &
748
                                (/gridy(j-1),gridy(j-1),gridy(j)/), &
749
                                (/zval(j-1,1),zval(j-1,2),zval(j,2)/))
750
         call glNormal3fv(normal)
751
         if (surface_color == rainbow_surface) then
752
            call get_rainbow(zval(j-1,1),minz,maxz,color)
753
            call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,color)
754
         endif
755
         call glvertex3d(gridx(i-1),gridy(j-1),zval(j-1,1))
756
         if (surface_color == rainbow_surface) then
757
            call get_rainbow(zval(j-1,2),minz,maxz,color)
758
            call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,color)
759
         endif
760
         call glvertex3d(gridx(i  ),gridy(j-1),zval(j-1,2))
761
         if (surface_color == rainbow_surface) then
762
            call get_rainbow(zval(j,2),minz,maxz,color)
763
            call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,color)
764
         endif
765
         call glvertex3d(gridx(i  ),gridy(j  ),zval(j  ,2))
766
         normal = normcrossprod((/gridx(i-1),gridx(i-1),gridx(i)/), &
767
                                (/gridy(j),gridy(j-1),gridy(j)/), &
768
                                (/zval(j,1),zval(j-1,1),zval(j,2)/))
769
         call glNormal3fv(normal)
770
         if (surface_color == rainbow_surface) then
771
            call get_rainbow(zval(j,2),minz,maxz,color)
772
            call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,color)
773
         endif
774
         call glvertex3d(gridx(i  ),gridy(j  ),zval(j  ,2))
775
         if (surface_color == rainbow_surface) then
776
            call get_rainbow(zval(j-1,1),minz,maxz,color)
777
            call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,color)
778
         endif
779
         call glvertex3d(gridx(i-1),gridy(j-1),zval(j-1,1))
780
         if (surface_color == rainbow_surface) then
781
            call get_rainbow(zval(j,1),minz,maxz,color)
782
            call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,color)
783
         endif
784
         call glvertex3d(gridx(i-1),gridy(j  ),zval(j  ,1))
785
786
      end do
787
   end do
788
789
   call glEnd
790
791
! if the surface is white, reset the lighting conditions
792
793
   if (surface_color == white_surface) then
794
      call glEnable(gl_light0)
795
      call glLightModelfv(gl_light_model_ambient, (/.5,.5,.5,1./))
796
   endif
797
   
798
799
endif ! draw_surface_solid
800
801
! draw the surface grid
802
803
if (draw_surface_grid) then
804
805
   call glPolygonMode(gl_front_and_back, gl_line)
806
   call glBegin(gl_triangles)
807
808
! draw surface grid in black
809
810
   call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,black)
811
812
! compute the function values for the first line of points
813
814
   do j=0,ngridy
815
      zval(j,2) = func_to_plot(gridx(0),gridy(j))
816
   end do
817
818
! for each x grid interval...
819
820
   do i=1,ngridx
821
822
! copy left side function values from the right side of the previous interval
823
824
      zval(:,1) = zval(:,2)
825
826
! compute the function values for the right side of the interval
827
828
      do j=0,ngridy
829
         zval(j,2) = func_to_plot(gridx(i),gridy(j))
830
      end do
831
832
      minz = min(minz,minval(zval))
833
      maxz = max(maxz,maxval(zval))
834
835
! for each y grid interval ...
836
837
      do j=1,ngridy
838
839
! add two triangles to the display list
840
841
         call glvertex3d(gridx(i-1),gridy(j-1),zval(j-1,1))
842
         call glvertex3d(gridx(i  ),gridy(j-1),zval(j-1,2))
843
         call glvertex3d(gridx(i  ),gridy(j  ),zval(j  ,2))
844
         call glvertex3d(gridx(i  ),gridy(j  ),zval(j  ,2))
845
         call glvertex3d(gridx(i-1),gridy(j-1),zval(j-1,1))
846
         call glvertex3d(gridx(i-1),gridy(j  ),zval(j  ,1))
847
848
      end do
849
   end do
850
851
   call glEnd
852
853
endif ! draw_surface_grid
854
855
! draw the contour plot
856
857
if (draw_contour) then
858
859
   call glPolygonMode(gl_front_and_back, gl_line)
860
   call glBegin(gl_lines)
861
   call glNormal3fv((/0.0_glfloat, 0.0_glfloat, 1.0_glfloat/))
862
863
! draw the domain in black, also sets color to black for black_contour
864
865
   call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse,black)
866
   call glVertex3d(minx,miny,0.0_GLDOUBLE)
867
   call glVertex3d(maxx,miny,0.0_GLDOUBLE)
868
   call glVertex3d(maxx,miny,0.0_GLDOUBLE)
869
   call glVertex3d(maxx,maxy,0.0_GLDOUBLE)
870
   call glVertex3d(maxx,maxy,0.0_GLDOUBLE)
871
   call glVertex3d(minx,maxy,0.0_GLDOUBLE)
872
   call glVertex3d(minx,maxy,0.0_GLDOUBLE)
873
   call glVertex3d(minx,miny,0.0_GLDOUBLE)
874
875
! set the contour values
876
877
   if (contour_values_given) then
878
      contour_value = actual_contours
879
   else
880
      do i=1,num_contour
881
         contour_value(i) = minz+(maxz-minz)*(i-1)/real(num_contour-1,GLDOUBLE)
882
      end do
883
   endif
884
885
! compute the function values for the first line of points
886
887
   do j=0,ngridy
888
      zval(j,2) = func_to_plot(gridx(0),gridy(j))
889
   end do
890
891
! for each x grid interval...
892
893
   do i=1,ngridx
894
895
! copy left side function values from the right side of the previous interval
896
897
      zval(:,1) = zval(:,2)
898
899
! compute the function values for the right side of the interval
900
901
      do j=0,ngridy
902
         zval(j,2) = func_to_plot(gridx(i),gridy(j))
903
      end do
904
905
      minz = min(minz,minval(zval))
906
      maxz = max(maxz,maxval(zval))
907
908
! for each y grid interval ...
909
910
      do j=1,ngridy
911
912
! for two triangles
913
914
         do k=1,2
915
916
! set the vertices
917
918
            if (k==1) then
919
               x1 = gridx(i-1); y1 = gridy(j-1); z1 = zval(j-1,1)
920
               x2 = gridx(i  ); y2 = gridy(j-1); z2 = zval(j-1,2)
921
               x3 = gridx(i  ); y3 = gridy(j  ); z3 = zval(j  ,2)
922
            else
923
               x1 = gridx(i-1); y1 = gridy(j-1); z1 = zval(j-1,1)
924
               x2 = gridx(i-1); y2 = gridy(j  ); z2 = zval(j  ,1)
925
               x3 = gridx(i  ); y3 = gridy(j  ); z3 = zval(j  ,2)
926
            endif
927
928
! order the vertices by z value
929
930
            xt = x1; yt = y1; zt = z1
931
            if (z2 < z1) then
932
               xt = x1; yt = y1; zt = z1
933
               if (z3 < z1) then
934
                  if (z2 < z3) then
935
                     x1 = x2; y1 = y2; z1 = z2
936
                     x2 = x3; y2 = y3; z2 = z3
937
                  else
938
                     x1 = x3; y1 = y3; z1 = z3
939
                  endif
940
                  x3 = xt; y3 = yt; z3 = zt
941
               else
942
                  x1 = x2; y1 = y2; z1 = z2
943
                  x2 = xt; y2 = yt; z2 = zt
944
               endif
945
            elseif (z3 < z1) then
946
               x1 = x3; y1 = y3; z1 = z3
947
               x3 = x2; y3 = y2; z3 = z2
948
               x2 = xt; y2 = yt; z2 = zt
949
            elseif (z3 < z2) then
950
               xt = x2; yt = y2; zt = z2
951
               x2 = x3; y2 = y3; z2 = z3
952
               x3 = xt; y3 = yt; z3 = zt
953
            endif
954
955
! if z1==z3, the triangle is horizontal and no contours pass through it
956
957
            if (z1==z3) cycle
958
959
! for each contour value
960
961
            do cont = 1,num_contour
962
963
! see if it passes through this triangle
964
965
               if (contour_value(cont) < z1) cycle
966
               if (contour_value(cont) > z3) exit
967
968
! set the color for contours colored by solution
969
970
               if (contour_color == rainbow_contour) then
971
                  call get_rainbow(contour_value(cont),minz,maxz,color)
972
                  call glMaterialfv(gl_front_and_back,gl_ambient_and_diffuse, &
973
                                    color)
974
               endif
975
976
! see where it crosses the 1-3 edge
977
978
               frac = (contour_value(cont)-z1)/(z3-z1)
979
               xcross1 = (1.0_GLDOUBLE - frac)*x1 + frac*x3
980
               ycross1 = (1.0_GLDOUBLE - frac)*y1 + frac*y3
981
982
! see where it crosses one of the other edges
983
984
               if (contour_value(cont) == z2) then
985
                  xcross2 = x2
986
                  ycross2 = y2
987
               elseif (contour_value(cont) < z2) then
988
                  frac = (contour_value(cont)-z1)/(z2-z1)
989
                  xcross2 = (1.0_GLDOUBLE - frac)*x1 + frac*x2
990
                  ycross2 = (1.0_GLDOUBLE - frac)*y1 + frac*y2
991
               else
992
                  frac = (contour_value(cont)-z2)/(z3-z2)
993
                  xcross2 = (1.0_GLDOUBLE - frac)*x2 + frac*x3
994
                  ycross2 = (1.0_GLDOUBLE - frac)*y2 + frac*y3
995
               endif
996
997
! add the line segment to the display list
998
999
               call glVertex3d(xcross1,ycross1,0.0_GLDOUBLE)
1000
               call glVertex3d(xcross2,ycross2,0.0_GLDOUBLE)
1001
1002
            end do
1003
         end do
1004
      end do
1005
   end do
1006
1007
   call glEnd
1008
1009
endif ! draw_contour
1010
1011
! finish off display list
1012
1013
call glEndList
1014
call glutPostRedisplay
1015
1016
end subroutine draw_func
1017
1018
subroutine get_rainbow(val,minval,maxval,c)
1019
real(GLDOUBLE), intent(in) :: val,maxval,minval
1020
real(GLFLOAT), intent(out) :: c(4)
1021
1022
real(GLFLOAT) :: f
1023
1024
if (maxval > minval) then
1025
   f = (val-minval)/(maxval-minval)
1026
else ! probably maxval==minval
1027
   f = 0.5_glfloat
1028
endif
1029
1030
if (f < .25) then
1031
   c(1) = 0.0_glfloat
1032
   c(2) = 4.0_glfloat * f
1033
   c(3) = 1.0_glfloat
1034
   c(4) = 1.0_glfloat
1035
elseif (f < .5) then
1036
   c(1) = 0.0_glfloat
1037
   c(2) = 1.0_glfloat
1038
   c(3) = 2.0_glfloat - 4.0_glfloat*f
1039
   c(4) = 1.0_glfloat
1040
elseif (f < .75) then
1041
   c(1) = 4.0_glfloat * f - 2.0_glfloat
1042
   c(2) = 1.0_glfloat
1043
   c(3) = 0.0_glfloat
1044
   c(4) = 1.0_glfloat
1045
else
1046
   c(1) = 1.0_glfloat
1047
   c(2) = 4.0_glfloat - 4.0_glfloat*f
1048
   c(3) = 0.0_glfloat
1049
   c(4) = 1.0_glfloat
1050
endif
1051
1052
end subroutine get_rainbow
1053
1054
function normcrossprod(x,y,z)
1055
real(glfloat), dimension(3) :: normcrossprod
1056
real(gldouble), dimension(3), intent(in) :: x,y,z
1057
real(glfloat) :: t1(3),t2(3),norm
1058
t1(1) = x(2) - x(1)
1059
t1(2) = y(2) - y(1)
1060
t1(3) = z(2) - z(1)
1061
t2(1) = x(3) - x(1)
1062
t2(2) = y(3) - y(1)
1063
t2(3) = z(3) - z(1)
1064
normcrossprod(1) = t1(2)*t2(3) - t1(3)*t2(2)
1065
normcrossprod(2) = t1(3)*t2(1) - t1(1)*t2(3)
1066
normcrossprod(3) = t1(1)*t2(2) - t1(2)*t2(1)
1067
norm = sqrt(dot_product(normcrossprod,normcrossprod))
1068
if (norm /= 0._glfloat) normcrossprod = normcrossprod/norm
1069
end function normcrossprod
1070
1071
subroutine menu_handler2(selection) bind(c,name="")
1072
integer(kind=glcint), value :: selection
1073
1074
select case (selection)
1075
1076
case (surfgrid_toggle)
1077
   draw_surface_grid = .not. draw_surface_grid
1078
   call draw_func
1079
1080
case (surfsolid_toggle)
1081
   draw_surface_solid = .not. draw_surface_solid
1082
   call draw_func
1083
1084
case (contour_toggle)
1085
   draw_contour = .not. draw_contour
1086
   call draw_func
1087
1088
case (quit_selected)
1089
   stop
1090
1091
end select
1092
1093
return
1094
end subroutine menu_handler2
1095
1096
subroutine param_handler(selection) bind(c,name="")
1097
integer(kind=glcint), value :: selection
1098
1099
select case (selection)
1100
1101
case (set_nx)
1102
   print *,"Enter number of x subintervals:"
1103
   read *, ngridx
1104
   call draw_func
1105
1106
case (set_ny)
1107
   print *,"Enter number of y subintervals:"
1108
   read *, ngridy
1109
   call draw_func
1110
1111
case (set_ncontour)
1112
   print *,"Enter number of contour lines:"
1113
   read *, num_contour
1114
   contour_values_given = .false.
1115
   call draw_func
1116
1117
case (set_contour_val)
1118
   print *,"enter number of contours:"
1119
   read *, num_contour
1120
   if (allocated(actual_contours)) deallocate(actual_contours)
1121
   allocate(actual_contours(num_contour))
1122
   print *,"enter ",num_contour," contour values:"
1123
   read *,actual_contours
1124
   contour_values_given = .true.
1125
   call draw_func
1126
1127
case (set_xrange)
1128
   print *,"Enter minimum and maximum x values:"
1129
   read *,minx,maxx
1130
   call draw_func
1131
1132
case (set_yrange)
1133
   print *,"Enter minimum and maximum y values:"
1134
   read *,miny,maxy
1135
   call draw_func
1136
1137
case (reset_params)
1138
   ngridx = init_ngridx
1139
   ngridy = init_ngridy
1140
   num_contour = init_num_contour
1141
   contour_color = init_contour_color
1142
   surface_color = init_surface_color
1143
   minx = init_minx
1144
   maxx = init_maxx
1145
   miny = init_miny
1146
   maxy = init_maxy
1147
   draw_surface_grid = init_draw_surface_grid
1148
   draw_surface_solid = init_draw_surface_solid
1149
   draw_contour = init_draw_contour
1150
   call draw_func
1151
1152
end select
1153
1154
end subroutine param_handler
1155
1156
subroutine contour_color_handler(selection) bind(c,name="")
1157
integer(kind=glcint), value :: selection
1158
1159
contour_color = selection
1160
call draw_func
1161
1162
end subroutine contour_color_handler
1163
1164
subroutine surface_color_handler(selection) bind(c,name="")
1165
integer(kind=glcint), value :: selection
1166
1167
surface_color = selection
1168
call draw_func
1169
1170
end subroutine surface_color_handler
1171
1172
subroutine make_menu(submenuid)
1173
integer, intent(in) :: submenuid
1174
integer :: menuid, param_id, contour_color_menu, surface_color_menu
1175
1176
contour_color_menu = glutCreateMenu(contour_color_handler)
1177
call glutAddMenuEntry("black"//char(0),black_contour)
1178
call glutAddMenuEntry("contour value"//char(0),rainbow_contour)
1179
1180
surface_color_menu = glutCreateMenu(surface_color_handler)
1181
call glutAddMenuEntry("red"//char(0),red_surface)
1182
call glutAddMenuEntry("white"//char(0),white_surface)
1183
call glutAddMenuEntry("surface height"//char(0),rainbow_surface)
1184
1185
param_id = glutCreateMenu(param_handler)
1186
call glutAddMenuEntry("number of x grid intervals"//char(0),set_nx)
1187
call glutAddMenuEntry("number of y grid intervals"//char(0),set_ny)
1188
call glutAddMenuEntry("number of uniform contour lines"//char(0),set_ncontour)
1189
call glutAddMenuEntry("contour values"//char(0),set_contour_val)
1190
call glutAddMenuEntry("x range"//char(0),set_xrange)
1191
call glutAddMenuEntry("y range"//char(0),set_yrange)
1192
call glutAddSubMenu("contour color"//char(0),contour_color_menu)
1193
call glutAddSubMenu("surface color"//char(0),surface_color_menu)
1194
call glutAddMenuEntry("reset to initial parameters"//char(0),reset_params)
1195
1196
menuid = glutCreateMenu(menu_handler2)
1197
call glutAddSubMenu("View Modifier"//char(0),submenuid)
1198
call glutAddMenuEntry("toggle surface grid"//char(0),surfgrid_toggle)
1199
call glutAddMenuEntry("toggle solid surface"//char(0),surfsolid_toggle)
1200
call glutAddMenuEntry("toggle contour"//char(0),contour_toggle)
1201
call glutAddSubMenu("plotting parameters"//char(0),param_id)
1202
call glutAddMenuEntry("quit"//char(0),quit_selected)
1203
call glutAttachMenu(GLUT_RIGHT_BUTTON)
1204
end subroutine make_menu
1205
1206
end module function_plotter
1207
1208
!---------------------------------------------------------------------------
1209
1210
program plot_func
1211
1212
use opengl_gl
1213
use opengl_glut
1214
use view_modifier
1215
use function_plotter
1216
implicit none
1217
1218
integer :: winid, menuid, submenuid
1219
1220
! Initializations
1221
1222
call glutInit
1223
call glutInitDisplayMode(ior(GLUT_DOUBLE,ior(GLUT_RGB,GLUT_DEPTH)))
1224
call glutInitWindowSize(500_glcint,500_glcint)
1225
1226
! Create a window
1227
1228
winid = glutCreateWindow("Function plotter")
1229
1230
! initialize view_modifier, receiving the id for it's submenu
1231
1232
submenuid = view_modifier_init()
1233
1234
! create the menu
1235
1236
call make_menu(submenuid)
1237
1238
! Set the display callback
1239
1240
call glutDisplayFunc(display)
1241
1242
! set the lighting conditions
1243
1244
call glClearColor(0.9_glclampf, 0.9_glclampf, 0.9_glclampf, 1.0_glclampf)
1245
call glLightfv(gl_light0, gl_diffuse, (/1.,1.,1.,1./))
1246
call glLightfv(gl_light0, gl_position, (/1.5,-.5,2.,0.0/))
1247
call glEnable(gl_lighting)
1248
call glEnable(gl_light0)
1249
call glLightModelfv(gl_light_model_ambient, (/.5,.5,.5,1./))
1250
call glDepthFunc(gl_lequal)
1251
call glEnable(gl_depth_test)
1252
1253
! Create the image
1254
1255
call draw_func
1256
1257
! Let glut take over
1258
1259
call glutMainLoop
1260
1261
end program plot_func
1262
1263
!---------------------------------------------------------------------------
1264
1265
! The function to plot
1266
1267
function func_to_plot(x,y)
1268
use opengl_gl
1269
real(GLDOUBLE) :: func_to_plot
1270
real(GLDOUBLE), intent(in) :: x,y
1271
1272
func_to_plot = 0.5_GLDOUBLE * ( &
1273
       cos(0.3_GLDOUBLE*sqrt(80.0_GLDOUBLE*x)-16.0_GLDOUBLE*y/3.0_GLDOUBLE)* &
1274
       cos(16.0_GLDOUBLE*x/3.0_GLDOUBLE) + x-y )
1275
1276
end function func_to_plot