#!/usr/local/bin/perl # # villarceau.pl : generates a torus by its Villarceau circles # (c) 2013 jl_morel@bribes.org - http://http://bribes.org/perl use strict; use warnings; use OpenGL qw/ :all /; my @light0_position = ( 2.0, 8.0, 2.0, 0.0 ); my @mat_amb_diff_color = ( 0.9, 0.7, 0.5, 1 ); my @light_diffuse = ( 1.0, 1.0, 1.0, 1 ); my @light_ambient = ( 0.15, 0.15, 0.15, 1 ); #------ Base vectors my @i = ( 1, 0, 0 ); my @j = ( 0, 1, 0 ); my @k = ( 0, 0, 1 ); #------ Initialization routine sub init { glClearColor( 1, 1, 1, 1 ); # White background glShadeModel(GL_SMOOTH); # Smooth shading glEnable(GL_DEPTH_TEST); # Enable hidden surface removal glEnable(GL_MULTISAMPLE); # Enable multisample antialiasing glEnable(GL_LIGHTING); # Enable lighting glEnable(GL_LIGHT0); glLightfv_p( GL_LIGHT0, GL_POSITION, @light0_position ); glLightfv_p( GL_LIGHT0, GL_DIFFUSE, @light_diffuse ); glLightfv_p( GL_LIGHT0, GL_AMBIENT, @light_ambient ); glMaterialfv_p( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, @mat_amb_diff_color ); glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); glEnable(GL_COLOR_MATERIAL); # Material track the current color } #------ Draw a circle in space # We draw a circle in space as a torus with a small inner radius my $R = 3; # Outer radius of the torus my $r = 0.08; # Inner radius of the torus. my $beta = 20; # Inclination my $spin = 0.0; # Rotation angle sub Drawcircle { my $alpha = shift; glPushMatrix(); glRotatef( $alpha, @k ); # Movement of the torus glTranslatef( @j ); # Translation and inclination glRotatef( $beta, @j ); # of the plane of the circle glutSolidTorus( $r, $R, 10, 100 ); # The circle! glPopMatrix(); } #------ Draw the torus sub display { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLightfv_p( GL_LIGHT0, GL_POSITION, @light0_position ); glLoadIdentity(); gluLookAt( 2.0, 4.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ); glPushMatrix(); glRotatef( $spin, @j ); for ( my $a = 0 ; $a < 360 ; $a += 10 ) { # 36 circles glColor3f( Rainbow( $a / 360 ) ); # of 36 different colors Drawcircle($a); } glPopMatrix(); glutSwapBuffers(); # debug code # if ( ( my $e = glGetError() ) != GL_NO_ERROR ) { # print "error : ", gluErrorString($e), "\n"; # } } #------ GLUT Callback called when the window is resized sub reshape { my ( $w, $h ) = @_; glViewport( 0, 0, $w, $h ); glMatrixMode(GL_PROJECTION); glLoadIdentity(); # define the projection gluPerspective( 45.0, $h ? $w / $h : 0, 1.0, 20.0 ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } #------ Routine for rotating the torus my $WaitUntil = 0; sub spinDisplay { my $TimeNow = glutGet(GLUT_ELAPSED_TIME); if ( $TimeNow >= $WaitUntil ) { $spin += 1.0; $spin = $spin - 360.0 if ( $spin > 360.0 ); glutPostRedisplay(); $WaitUntil = $TimeNow + 1000 / 25; # 25 frames/s } } #------ Main glutInit(); glutInitDisplayMode( GLUT_DOUBLE # Double buffering | GLUT_RGB # RGB mode | GLUT_DEPTH # Hidden surface removal | GLUT_MULTISAMPLE # Multisample antialiasing ); glutInitWindowPosition( 0, 0 ); glutInitWindowSize( 300, 300 ); glutCreateWindow("Torus"); init(); glutDisplayFunc( \&display ); glutReshapeFunc( \&reshape ); glutIdleFunc( \&spinDisplay ); glutMainLoop(); #------ Rainbow color map function # Usage: ($red, $green, $blue) = Rainbow( $x ); # $x must be between 0 and 1. # Returns the color of the rainbow (RGB list) associated with $x # from blue for $x = 0 to red for $x = 1. sub max { $_[0] < $_[1] ? $_[1] : $_[0] } # max auxiliary function sub Rainbow { my $dx = 0.8; my $s = ( 6 - 2 * $dx ) * $_[0] + $dx; return max( 0, ( 3 - abs( $s - 4 ) - abs( $s - 5 ) ) / 2 ), # Red max( 0, ( 4 - abs( $s - 2 ) - abs( $s - 4 ) ) / 2 ), # Green max( 0, ( 3 - abs( $s - 1 ) - abs( $s - 2 ) ) / 2 ); # Blue }