BP

Previous

Next


use OpenGL;

#2 The notched cube

This script shows an animation of a cube that turns into an anticube (or stella octangula).
The notched cube construction, from a part of a dihedron, is shown in the second animated picture.

 rotation   construction

notched.pl


#!/usr/local/bin/perl
#
# nodched.pl : from cube to anticube (stella octangula)
# (c) 2013 jl_morel@bribes.org - http://http://bribes.org/perl
use strict;
use warnings;
use OpenGL qw/ :all /;

# ============ Some math routines

#------ Returns the cross product of 2 vectors

sub CrossProduct {
  return (
    $_[1] * $_[5] - $_[2] * $_[4],
    $_[3] * $_[2] - $_[0] * $_[5],
    $_[0] * $_[4] - $_[1] * $_[3]
  );
}

#------ Returns the length of a vector

sub GetVectorLength {
  return sqrt( $_[0] * $_[0] + $_[1] * $_[1] + $_[2] * $_[2] );
}

#------ Returns the vector scaled by the last parameter

sub ScaleVector {
  return ( $_[0] * $_[3], $_[1] * $_[3], $_[2] * $_[3] );
}

#------ Returns a normalized vector (length = 1)

sub NormalizeVector {
  return ( 0, 0, 0 ) if ( my $norm = GetVectorLength(@_) ) == 0;
  return ScaleVector( @_, 1 / $norm );
}

#------ Returns the unit normal vector of a triangle specified by the three
# points P1, P2, and P3.

sub FindUnitNormal {
  return NormalizeVector(
    CrossProduct(
      $_[0] - $_[3], $_[1] - $_[4], $_[2] - $_[5],    # P1-P2
      $_[3] - $_[6], $_[4] - $_[7], $_[5] - $_[8]     # P2-P3
    )
  );
}

#------ Base vectors

my @i = ( 1, 0, 0 );
my @j = ( 0, 1, 0 );
my @k = ( 0, 0, 1 );

#============ End math routines

my $spin  = 0;        # rotation angle
my $k     = 0;        # notch depth  0<$k<1
my $delta = 0.005;    # increment for $k

my @light0_position    = ( 2,    8,    2,    0 );
my @mat_amb_diff_color = ( 1,    0.7,  0.5,  0 );
my @light_diffuse      = ( 1,    1,    1,    1 );
my @light_ambient      = ( 0.15, 0.15, 0.15, 0.15 );

#------ Draw only a diedre (1/12th of the cube)

sub DrawDiedre {

  my $h = 1 - $k;
  my @A = ( -1, 1, 1 );
  my @B = ( 1, 1, 1 );
  my @C = ( 0, 1, 0 );
  my @D = ( 0, 0, 1 );
  my @E = ( -$k, 1, 1 );
  my @F = ( $k, 1, 1 );
  my @G = ( 0, 1, $h );
  my @H = ( 0, $h, 1 );

  glBegin(GL_QUADS);    # The upper polygon is concave,
    glNormal3d(@j);     # we draw it with two convex quads.
    glVertex3d(@A);     # We don't want to use a tessallator!
    glVertex3d(@E);
    glVertex3d(@G);
    glVertex3d(@C);

    glVertex3d(@F);
    glVertex3d(@B);
    glVertex3d(@C);
    glVertex3d(@G);

    glNormal3d(@k);     # idem for the front polygon
    glVertex3d(@A);
    glVertex3d(@D);
    glVertex3d(@H);
    glVertex3d(@E);

    glVertex3d(@B);
    glVertex3d(@F);
    glVertex3d(@H);
    glVertex3d(@D);
  glEnd();

                        # the two triangles of the notch
  glBegin(GL_TRIANGLES);
    glNormal3d( FindUnitNormal( @E, @H, @G ) );
    glVertex3d(@E);
    glVertex3d(@H);
    glVertex3d(@G);

    glNormal3d( FindUnitNormal( @H, @F, @G ) );
    glVertex3d(@H);
    glVertex3d(@F);
    glVertex3d(@G);
  glEnd();
}

#------ Draw the notched cube

sub display {
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  glLightfv_p( GL_LIGHT0, GL_POSITION, @light0_position );
  glLoadIdentity();
  gluLookAt( 2, 4, 10, 0, 0, 0, 0, 1, 0 );
  glPushMatrix();
    glScalef( 2, 2, 2 );
    glRotatef( $spin, @j );

    DrawDiedre();               # The cube is built with 11 rotations
    glRotatef( 90, @j );
    DrawDiedre();
    glRotatef( 90, @j );
    DrawDiedre();
    glRotatef( 90, @j );
    DrawDiedre();
    glRotatef( 90, @i );
    DrawDiedre();
    glRotatef( 90, @j );
    DrawDiedre();
    glRotatef( 90, @k );
    DrawDiedre();
    glRotatef( 90, @k );
    DrawDiedre();
    glRotatef( 90, @j );
    DrawDiedre();
    glRotatef( 90, @j );
    DrawDiedre();
    glRotatef( 90, @i );
    DrawDiedre();
    glRotatef( -90, @j );
    DrawDiedre();
  glPopMatrix();
  glutSwapBuffers();

  # debug code
  #  if ( ( my $e = glGetError() ) != GL_NO_ERROR ) {
  #    print "error : ", gluErrorString($e), "\n";
  #  }
}

#------ Initialization routine

sub init {
  glClearColor( 0, 0, 0, 0 );     # Black background
  glShadeModel(GL_SMOOTH);        # Smooth shading
  glEnable(GL_MULTISAMPLE);       # Enable multisample antialiasing
  glEnable(GL_DEPTH_TEST);        # Enable hidden surface removal
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
                                  # Light and material
  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 );
}

#------ GLUT Callback called when the window is resized

sub reshape {
  my ( $w, $h ) = @_;
  glViewport( 0, 0, $w, $h );
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective( 45, $h ? $w / $h : 0, 1, 20 );
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

#------ Routine for rotating the cube

my $WaitUntil = 0;

sub spinDisplay {
  my $TimeNow = glutGet(GLUT_ELAPSED_TIME);
  if ( $TimeNow >= $WaitUntil ) {
    $spin += 1;
    $delta = -$delta if $k > 1 or $k < 0;
    $k += $delta;
    $spin = $spin - 360 if ( $spin > 360 );
    glutPostRedisplay();
    $WaitUntil = $TimeNow + 1000 / 25;    # 25 frames/s
  }
}

#------ GLUT callback for the mouse

sub mouse {
  my ( $button, $state, $x, $y ) = @_;
  if ( $button == GLUT_LEFT_BUTTON ) {
    glutIdleFunc( \&spinDisplay ) if ( $state == GLUT_DOWN );
  }
  elsif ( $button == GLUT_RIGHT_BUTTON ) {
    glutIdleFunc(undef) if ( $state == GLUT_DOWN );
  }
}

#------ Main program

glutInit();
glutInitDisplayMode(
    GLUT_DOUBLE         # Double buffering
  | GLUT_RGB            # RGB color mode
  | GLUT_DEPTH          # Hidden surface removal
  | GLUT_MULTISAMPLE    # Multisample antialiasing
);
glutInitWindowSize( 300, 300 );
glutCreateWindow("Notched cube");
init();
glutDisplayFunc( \&display );
glutReshapeFunc( \&reshape );
glutMouseFunc( \&mouse );
glutIdleFunc( \&spinDisplay );
glutMainLoop();


The script as .txt for download: notched.pl.txt

Back to Top


BP 2013 J-L Morel - Contact : jl_morel@bribes.org [Validation HTML 4.0!]