Drawing a duoprism

Posted on July 8, 2019 by Stéphane Laurent

In geometry of 4 dimensions, a duoprism is a polytope resulting from the Cartesian product of two polygons. We show how to draw a duoprism using R, Asymptote and POV-Ray, when the two polygons are regular.

Drawing a duoprism with R

Drawing a duoprism with Asymptote

settings.render = 4;
settings.outformat = "pdf";
import solids;
import tube;
size(200,0);

currentprojection=orthographic(4,4,4);
currentlight = light(gray(0.85), ambient=black, specularfactor=3,
                     (100,100,100), specular=gray(0.9), viewport=false);
currentlight.background = rgb("363940ff");

// lexicographic order ---------------------------------------------------------
bool dominates(int[] e1, int[] e2){
    return e2[0]>e1[0] || (e2[0]==e1[0] && e2[1]>e1[1]);
}

// vertices --------------------------------------------------------------------
int A = 30;
int B = 4;
real[][] poly1 = new real[A][2];
for(int i = 0; i < A; ++i){
    poly1[i][0] = cos(i/A*2pi);
    poly1[i][1] = sin(i/A*2pi);
}
real[][] poly2 = new real[B][2];
for(int i = 0; i < B; ++i){
    poly2[i][0] = cos(i/B*2pi);
    poly2[i][1] = sin(i/B*2pi);
}
real[][][] vertices = new real[A][B][4];
for(int i = 0; i < A; ++i){
    for(int j = 0; j < B; ++j){
        vertices[i][j] = concat(poly1[i],poly2[j]);
    }
}

// edges -----------------------------------------------------------------------
int[][][] edges;
for(int i = 0; i < A; ++i){
    for(int j = 0; j < B; ++j){
        int[] e = {i,j};
        int[] candidate = {i,(j-1)%B};
        if(dominates(e,candidate)){
            int[][] edge = {e,candidate}; 
            edges.push(edge);
        }
        int[] candidate = {i,(j+1)%B};
        if(dominates(e,candidate)){
            int[][] edge = {e,candidate}; 
            edges.push(edge);
        }
        int[] candidate = {(i-1)%A,j};
        if(dominates(e,candidate)){
            int[][] edge = {e,candidate}; 
            edges.push(edge);
        }
        int[] candidate = {(i+1)%A,j};
        if(dominates(e,candidate)){
            int[][] edge = {e,candidate}; 
            edges.push(edge);
        }
    }
}

// stereographic projection ----------------------------------------------------
triple stereog(real[] x, real r){
  return (x[0],x[1],x[2])/(r-x[3]);
}

// projected vertices ----------------------------------------------------------
triple[][] vs = new triple[A][B];
for(int i = 0; i < A; ++i){
    for(int j = 0; j < B; ++j){
        vs[i][j] = stereog(vertices[i][j], sqrt(2));
    }
}

// draw the duoprism -----------------------------------------------------------
// draw edges
for(int k = 0; k < edges.length; ++k){
    path3 p = 
      vs[edges[k][0][0]][edges[k][0][1]]..vs[edges[k][1][0]][edges[k][1][1]];
    draw(tube(p, scale(0.05)*unitcircle), 
            material(rgb(255,215,0), emissivepen=gray(0.1)));
}
// draw vertices
for(int i = 0; i < A; ++i){
    for(int j = 0; j < B; ++j){
        draw(shift(vs[i][j])*scale3(0.1)*unitsphere, rgb(255,215,0));
    }
}

Drawing a duoprism with POV-Ray

#version 3.7;

global_settings { assumed_gamma 1 } 

#include "colors.inc"
#include "textures.inc"

// camera ----------------------------------------------------------------------
camera {
    location <0, 0,-10>
    look_at 0
    angle 45
}

// light sources ---------------------------------------------------------------
light_source { <0,0,-100> White shadowless} 
light_source { <100,0,-100> White shadowless} 

// moon ------------------------------------------------------------------------
light_source{ 
    <-1000, 800, 3000> 
    color White
    shadowless
    looks_like{ 
        sphere{ 
            <0,0,0>,300 
            texture { 
                pigment { 
                    color White 
                }
                normal { 
                    bumps 0.5
                    scale 50
                }
                finish { 
                    emission 0.8   
                    diffuse 0.2
                    phong 1
                }                                        
            } 
        } 
    } 
} 

// sky -------------------------------------------------------------------------
plane { 
    <0,1,0>,1 hollow  
    texture { 
        pigment { 
            color rgb <0.01, 0.01, 0.2> 
        }
        finish { 
            emission 0.5 
            diffuse 0.5 
        } 
    }  
    scale 10000
}

// the clouds ------------------------------------------------------------------
plane { 
    <0,1,0>,1 hollow  
    texture { 
        pigment{ 
            bozo turbulence 1.3
            color_map { 
                [0.00 rgb <0.24, 0.32, 1.0>*0.6]
                [0.75 rgb <0.24, 0.32, 1.0>*0.6]
                [0.83 rgb <1,1,1>              ]
                [0.95 rgb <0.25,0.25,0.25>     ]
                [1.00 rgb <0.5,0.5,0.5>        ]
            }
            scale 2.5
            translate <0,1,0>
        }
        finish { 
            emission 0.25 
            diffuse 0
        } 
    }      
    scale 5000
}

// fog on the ground -----------------------------------------------------------
fog { 
    fog_type   2
    distance   50
    color      Gray50  
    fog_offset 0.1
    fog_alt    1.5
    turbulence 1.8
}

// sea -------------------------------------------------------------------------
plane { 
    <0,1,0>, -1 hollow
    texture{
        pigment{
            rgb <.098,.098,.439>
        }
        finish {
            ambient 0.15
            diffuse 0.55
            brilliance 6.0
            phong 0.8
            phong_size 120
            reflection 0.2
        }
        normal { 
            bumps 0.95
            turbulence .05 
            scale <1,0.25,1> 
        }
    }
}

// vertices --------------------------------------------------------------------
#declare A = 23;
#declare B = 29;
#declare poly1 = array[A];
#for(i,0,A-1)
    #declare poly1[i] = array[2] {cos(i/A*2*pi), sin(i/A*2*pi)};
#end
#declare poly2 = array[B];
#for(i,0,B-1)
    #declare poly2[i] = array[2] {cos(i/B*2*pi), sin(i/B*2*pi)};
#end
#declare vertices = array[A][B];
#for(i,0,A-1)
    #for(j,0,B-1)
        #declare vertices[i][j] = 
            < poly1[i][0], poly1[i][1], poly2[j][0], poly2[j][1] >;
    #end
#end

// edges -----------------------------------------------------------------------
#macro dominates(e1,e2)
    (e2[0]>e1[0]) | ((e2[0]=e1[0]) & (e2[1]>e1[1]))
#end
#declare nedges = 2*A*B;
#declare edges = array[nedges];
#declare k=0;
#for(i,0,A-1)
    #for(j,0,B-1)
        #local e = array[2] {i,j};
        #local candidate = array[2] {i,mod(mod(j-1,B)+B,B)};
        #if(dominates(e,candidate))
            #local edge = array[2] {e,candidate};
            #declare edges[k] = edge;
            #declare k = k+1;
        #end
        #local candidate = array[2] {i,mod(mod(j+1,B)+B,B)};
        #if(dominates(e,candidate))
            #local edge = array[2] {e,candidate};
            #declare edges[k] = edge;
            #declare k = k+1;
        #end
        #local candidate = array[2] {mod(mod(i-1,A)+A,A),j};
        #if(dominates(e,candidate))
            #local edge = array[2] {e,candidate};
            #declare edges[k] = edge;
            #declare k = k+1;
        #end
        #local candidate = array[2] {mod(mod(i+1,A)+A,A),j};
        #if(dominates(e,candidate))
            #local edge = array[2] {e,candidate};
            #declare edges[k] = edge;
            #declare k = k+1;
        #end
    #end
#end

// macros ----------------------------------------------------------------------
#macro rotate4d(theta,phi,xi,vec)
    #local a = cos(xi);
    #local b = sin(theta)*cos(phi)*sin(xi);
    #local c = sin(theta)*sin(phi)*sin(xi);
    #local d = cos(theta)*sin(xi);
    #local p = vec.x;
    #local q = vec.y;
    #local r = vec.z;
    #local s = vec.t;
    < a*p - b*q - c*r - d*s
    , a*q + b*p + c*s - d*r
    , a*r - b*s + c*p + d*q
    , a*s + b*r - c*q + d*p >
#end

#macro StereographicProjection(q) 
    acos(q.t/sqrt(2))/sqrt(2-q.t*q.t) * <q.x,q.y,q.z>
#end

#macro ProjectedVertices(theta,phi,xi)
    #local out = array[A][B];
    #for(i,0,A-1)
        #for(j,0,B-1)
            #local out[i][j] = StereographicProjection(
                                 rotate4d(theta,phi,xi,vertices[i][j])
                               );
        #end
    #end
    out
#end

// texture ---------------------------------------------------------------------
#declare edgeTexture = 
    texture {
      New_Penny
      finish {
        ambient 0.01
        diffuse 2
        reflection 0
        brilliance 8
        specular 0.1
        roughness 0.1
      }
    };

// draw an edge ----------------------------------------------------------------
#macro Edge(verts,pair1,pair2)
    cylinder { 
        verts[pair1[0]][pair1[1]] verts[pair2[0]][pair2[1]], 0.05
        texture { edgeTexture }
    }
#end

// draw ------------------------------------------------------------------------
#declare vs = ProjectedVertices(0, 0, 2*frame_number*pi/180);
object {
    union {
        #for(i,0,nedges-1)
            Edge(vs,edges[i][0],edges[i][1])
        #end
        #for(i,0,A-1)
            #for(j,0,B-1)
                sphere {
                    vs[i][j], 0.06
                    texture { edgeTexture }
                }
            #end
        #end  
    }
    scale 0.5
    rotate <90, 0, 0>
    translate <0, 0.5, -2>
}

/* ini file
Input_File_Name = Duoprism.pov
Initial_Clock = 0
Final_Clock = 1
Initial_Frame = 0
Final_Frame = 179
Cyclic_Animation = on
*/