BðP

Previous

Next


use OpenGL;

#6 Equidomoid

This program draws an Equidomoid.
An equidomoid is a non-polyhedral solid consisting of cylindrical wedges located symmetrically around an axis.
The equidomoids were studied (and named!) by Léopold Hugo, a nephew of Victor Hugo, in a series of strange monographs.
You can read online its essays on Hugodomoidal Geometry.
The equidomoid of order 4 is known as bicylinder or Steinmetz solid.
Equidomoids of order 3, 5 and 32:

 3-domoid  5-equidomoid  32-equidomoid

 hugo1  hugo2
 Figures from Émile Fourrey - Curiosités géométriques (Paris 1910)

domoid.pl


#!/usr/local/bin/perl
#
# domoid.pl : draw an equidomoid
# (c) 2013 jl_morel@bribes.org - http://http://bribes.org/perl
use strict;
use warnings;
use Math::Trig;
use OpenGL qw/ :all /;

my $n     = 6;      # equidomoid order
my $scale = 2.8;    # image scale

#------ Base vectors

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

#------ Draw a Cylindrical Wedge

sub DrawWedge {
  my $n = shift;

  # Setup the quadric object
  my $qd = gluNewQuadric();
  gluQuadricDrawStyle( $qd, GLU_FILL );
  gluQuadricNormals( $qd, GLU_SMOOTH );
  gluQuadricOrientation( $qd, GLU_OUTSIDE );
  gluQuadricTexture( $qd, GL_FALSE );

  # The cylinder is cut with two plans to get a wedge
  glPushMatrix();
    glClipPlane_p( GL_CLIP_PLANE0, sin( pi / $n ), 0, cos( pi / $n ), 0 );
    glEnable(GL_CLIP_PLANE0);
    glClipPlane_p( GL_CLIP_PLANE1, sin( pi / $n ), 0, -cos( pi / $n ), 0 );
    glEnable(GL_CLIP_PLANE1);
    glTranslatef( 0, 0, -2 );
    gluCylinder( $qd, 1, 1, 4, 50, 1 );
    glDisable(GL_CLIP_PLANE0);
    glDisable(GL_CLIP_PLANE1);
  glPopMatrix();
}

#------ Draw the equidomoid of order n

sub DrawEquidomoid {
  my $n = shift;
  for ( 0 .. $n - 1 ) {
    glRotatef( rad2deg( 2 * pi / $n ), @j );
    glColor3f( Rainbow( $_ / ( $n - 1 ) ) );
    DrawWedge($n);
  }
}

#------ Draw the bounding prism

sub DrawPrism {
  my $n     = shift;
  my $r     = 1 / cos( pi / $n );
  my @alpha = map { ( 2 * $_ - 1 ) * pi / $n } ( 0 .. $n - 1 );
  glColor3f( 0.8, 0.8, 0.8 );   # gray
  glBegin(GL_LINE_LOOP);        # bottom
    glVertex3f( $r * cos($_), -1, $r * sin($_) ) foreach @alpha;
  glEnd();
  glBegin(GL_LINE_LOOP);        # up
    glVertex3f( $r * cos($_), 1, $r * sin($_) ) foreach @alpha;
  glEnd();
  glBegin(GL_LINES);            # edges
    foreach (@alpha) {
      glVertex3f( $r * cos($_), 1,  $r * sin($_) );
      glVertex3f( $r * cos($_), -1, $r * sin($_) );
    }
  glEnd();
}

#------ Draw the scene

my $spin = 0;

sub display {
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  glLoadIdentity();
  gluLookAt( 2.0, 4.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 );
  glPushMatrix();

    glScalef( $scale, $scale, $scale );
    glRotatef( $spin, @j );

    DrawEquidomoid($n);
    DrawPrism($n);

  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, $h ? $w / $h : 0, 1, 20 );
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

#------ Routine for rotating the scene

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
  }
}

#------ 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 );
  }
}

#------ Initialization routine

my @light0_position    = ( -2.0, 8.0, 5.0, 0.0 );
my @mat_amb_diff_color = ( 0.8,  0.8, 0.8, 1.0 );
my @light_diffuse      = ( 2.0,  2.0, 2.0, 1.0 );
my @light_ambient      = ( 0.2,  0.2, 0.2, 1 );

sub init {
  glClearColor( 1, 1, 1, 1 );    # White 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_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 );
  glEnable(GL_COLOR_MATERIAL);    # Material track the current color
}

#------ Main

glutInit();
glutInitDisplayMode(
      GLUT_DOUBLE                 # Double buffering
    | GLUT_RGB                    # RGB color mode
    | GLUT_DEPTH                  # Hidden surface removal
    | GLUT_MULTISAMPLE            # Multisample antialiasing
);
glutInitWindowSize( 300, 300 );
glutCreateWindow("Equidomoid");
init();
glutDisplayFunc( \&display );
glutReshapeFunc( \&reshape );
glutMouseFunc( \&mouse );
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
}

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

Back to Top


BðP © 2013 J-L Morel - Contact : jl_morel@bribes.org [Validation HTML 4.0!]