//This file is licensed under the terms of the CC-LGPL // //Author: Maurizio Paolini // /*============================================================================ Rubik's Magic 8 tiles include file - version 2.0 ------------------------------------------------------------- Usage: RM_RubiksMagic("...") The parameter is a string containing the configuration description. Uses actual measures of the real Rubik's Magic. 1 pov unit = 1 cm The configuration description is a sequence of eight tokens, each describing how the i-th tile is connected to the (i-1)-th tile (see below). Tokens can be separated by spaces. Configurations are restricted to the so-called "polyominoids": connected sets made of unit squares with all vertices in the unit cube lattice. We so not allow for superposed tiles (they would compenetrate; support to avoid this is not implemented). Update: partial provisioning to work around the problem of superposed tiles has been added with the use of characters '+' and '-' that allow to 'lift' the following tile laterally of an amount given by half the tile thickness. Characters '+' and '-' can be repeated. As an example "-U--D-DL+R++R+UR" corresponds to the 'fish' configuration. Each tile carries an orientation: an arrow drawn on one of its faces (the front face) parallel to an edge. The eight tiles form a chain and are numbered from 0 to 7, adjacent sides are connected by a common edge and form an angle of 180 degrees (complanar) or 90 degrees, in which case the "fold" can be a "mountain fold" or a "valley fold" with respect to the front face of the two tiles. The orientation of consecutive tiles is mutually consistent, this means that after rotating the two tiles around the common edge to make them coincident, the two arrows will come into contact and will be perfectly coincident (same direction and sense). In the starting rectangular 2x4 configuration, the four lower tiles, number from 0 to 3 from left to right) have vertical arrows pointing up; the four upper tiles (numbered from 4 to 7 from right to left) have vertical arrows pointing down. Each token of the description consists of one or more ASCII characters, the first can be R, L, U, D (standing for Right, Left, Up, Down) and indicates which edge of the (i-1)-th tile connects it to the i-th tile. The second character, if present, can be "m", or "v", standing for "mountain" or "valley" fold and indicates a 90 degrees angle, otherwise the two tiles form a 180 degrees angle. An optional 'p' can appear as third character indicating a partial fold, the actual fold angle is obtained by multiplying 90 degrees by the value of the variable RM_Partial (it must be set before calling the macro). If the third character is 'P' then the multiplying factor is complemented as (1 - RM_Partial). It is possible to specify more than one multiplying factors by defining the appropriate entries of the array RM_Partial[] (starting from 0) and adding a corresponding fourth caracter as a digit ('0' to '9'). By default the first tile is tile number 0 oriented with its front face facing up, the arrow pointing in the z direction (pov-ray coordinates). It can be changed by defining the variable RM_StartTile. It is possible to orient the first tile differently by defining appropriately the variable RM_TilesRotation (-1, 0, 1, 2 for multiples of 90 degrees) and RM_FlipTiles to turn tiles upside-down. It is also possible to follow the chain of tiles backwards by defining RM_TilesIncrement as -1. A number of decorations are implemented, the default corresponds to the decoration of the puzzle available from the brand "Matchbox". Other possibilities include: black tiles, black tiles with a diagonal indicating the direction of the nylon wires, oriented tiles. --------------------------------------------------------------------------------- Acknowledgment: I largely took inspiration from WarpRubiksCube, in particular how to deal with the string argument to the macro and the general approach */ #declare RM_SizeMatchbox = 5.25; #declare RM_SizeDogs = 4.00; #declare RM_ThicknessMatchbox = 0.45; #declare RM_ThicknessDogs = 0.45; // unused! Provided just as an additional info #declare RM_Size = RM_SizeMatchbox; #declare RM_Thickness = RM_ThicknessMatchbox; #declare RM_GRadius = 0.1; #declare RM_HalfSize = RM_Size/2; #declare RM_HalfThickness = RM_Thickness/2; #declare RM_GRadiusRel = RM_GRadius/RM_Size; #declare RM_DecMatchbox = 0; #declare RM_DecLingao = 1; #declare RM_DecDogs = 2; #declare RM_DecBlack = 3; #declare RM_DecDiagonal = 4; #declare RM_DecMatchboxFast = 5; #declare RM_DecArrow = 6; #declare RM_DecTileId = 7; #declare RM_DecTransparent = 8; #declare RM_DecCustom = 9; #declare RM_decsnum = 10; #declare RM_numtiles = 8; #declare RM_Tile = array[8][RM_decsnum]; #declare RM_Scale = array[RM_decsnum]; #declare RM_Partials = array[10]; #declare RM_LiftAmounts = array[10]; #declare RM_tsmus = 0.3; // spessore strato centrale, relativ. allo spessore totale #declare RM_tsmus1 = RM_tsmus/2; #declare RM_tsmus2 = (1 - RM_tsmus)/2; // spessore smusso verticale #declare RM_smus = RM_tsmus2*RM_Thickness/RM_Size; // spessore smusso oriz, relativ. al lato tassello #declare RM_goffset = 0.25; #declare RM_secondfold = 89; /* * provision for puzzles with different size; * the RM_Scale vector contains the size relative to the size of the Matchbox brand puzzle. * The actual size will be (e.g.) RM_Size*RM_Scale[RM_Decoration] */ #ifndef (RM_ambient) #declare RM_ambient=0.3; #end #ifndef (RM_highresolution) #declare RM_highresolution=off; #end #ifndef (RM_LiftAmount) #declare RM_LiftAmount = 0.5*RM_Thickness; #end #local decid = 0; #while (decid < RM_decsnum) #declare RM_Scale[decid] = 1; #local decid = decid + 1; #end #declare RM_Scale[RM_DecDogs] = RM_SizeDogs/RM_SizeMatchbox; // From textures.inc (Glass2) #declare RM_TPlastic = texture { pigment { rgbf <1,1,1,0.95> } finish { ambient 0 diffuse 0 reflection 0.2 phong 0.3 phong_size 60 } } #declare RM_MPlastic = material {texture {RM_TPlastic} interior {ior 1.5}} #declare RM_TGrooves = texture { pigment { rgbf <1,1,1,0.5> } finish { ambient 0 diffuse 0 reflection 0.1 phong 0.3 phong_size 60 } } #declare RM_middle = box { <0,-1.0001*RM_tsmus1,0>,<1,1.0001*RM_tsmus1,1> } #declare RM_tapered = prism { conic_sweep linear_spline 1-RM_smus/2, 1, 5, <-1,-1>,<1,-1>,<1,1>,<-1,1>,<-1,-1> scale <0.5,1,0.5> translate -y+0.5*x+0.5*z scale <1,-2*RM_tsmus2/RM_smus,1> translate 0.99*RM_tsmus1*y } #declare RM_plastic0 = merge { object {RM_middle} object {RM_tapered} object {RM_tapered rotate 180*z translate x } translate -0.5*<1,0,1> scale } #declare RM_lgroove = cylinder { <-0.5-RM_goffset,0,-0.5>,<0.5,0,0.5+RM_goffset>, RM_GRadiusRel } #declare RM_sgroove = cylinder { <-0.5-RM_goffset,0,-0.5+2*RM_goffset>,<0.5-2*RM_goffset,0,0.5+RM_goffset>, RM_GRadiusRel } #declare RM_lgroovesup = union { object {RM_lgroove} object {RM_lgroove rotate 90*y} object {RM_lgroove rotate 180*y} object {RM_lgroove rotate -90*y} scale RM_Size } #declare RM_sgroovesup = union { object {RM_sgroove} object {RM_sgroove rotate 90*y} object {RM_sgroove rotate 180*y} object {RM_sgroove rotate -90*y} scale RM_Size } #declare RM_grooves = union { object {RM_lgroovesup translate RM_HalfThickness*y } object {RM_lgroovesup translate -RM_HalfThickness*y } object {RM_sgroovesup translate RM_HalfThickness*y } object {RM_sgroovesup translate -RM_HalfThickness*y } texture {RM_TGrooves} } #declare RM_plastic = difference { object {RM_plastic0} object {RM_grooves} material {RM_MPlastic} } #declare RM_arrowshape = union { box {<-0.02,0,-0.35>,<0.02,0.02,0.35>} box {<-0.02,0,-0.25>,<0.02,0.02,0> rotate 20*y translate 0.35*z } box {<-0.02,0,-0.25>,<0.02,0.02,0> rotate -20*y translate 0.35*z } } #declare RM_arrow = union { object {RM_arrowshape texture {pigment {rgb <1,1,0>}} } object {RM_arrowshape texture {pigment {rgb <0,0,1>}} translate -0.02*y } finish {ambient 0.2} } #macro RM_createtile_fast (tileidcrop, frontback) #if (tileidcrop < 4) #local maptransformf = transform {scale 4 translate -tileidcrop*x - 2*y}; #local maptransformb = transform {scale 4 translate -(3 - tileidcrop)*x}; #else #local maptransformf = transform {rotate 180*z translate x+y scale 4 translate - (tileidcrop - 4)*x}; #local maptransformb = transform {rotate 180*z translate x+y scale 4 translate - (7 - tileidcrop)*x - 2*y}; #end union { object {RM_middle pigment { rgb <0,0,0> } } object {RM_tapered texture { pigment { image_map {jpeg frontback} transform {maptransformf} rotate 90*x } } } object {RM_tapered texture { pigment { image_map {jpeg frontback} transform {maptransformb} rotate 90*x } } rotate 180*z translate x } translate -0.5*<1,0,1> scale finish { specular .5 roughness .1 ambient RM_ambient } } #end #macro RM_createtile_old (front) RM_createtileg (-1, front, 0, 0, -1) #end #macro RM_createtile (tileid, frontback) RM_createtileg (tileid, frontback, 0, 0, -1) #end /* * if tileidcrop >=0 the images need to be cropped from the front image * (back must be equal to front) */ #macro RM_createtileg (tileidcrop, frontback, tiletype, arrow, tid) #local maptransformf = transform {scale 1}; #local maptransformb = transform {scale 1}; #if (tileidcrop >= 0) #if (tileidcrop < 4) #local maptransformf = transform {scale 4 translate -tileidcrop*x - 2*y}; #local maptransformb = transform {scale 4 translate -(3 - tileidcrop)*x}; #else #local maptransformf = transform {rotate 180*z translate x+y scale 4 translate - (tileidcrop - 4)*x}; #local maptransformb = transform {rotate 180*z translate x+y scale 4 translate - (7 - tileidcrop)*x - 2*y}; #end #end union { object {RM_plastic} union { object {box {<0,0,0>,<1,0.01,1>} texture { pigment { image_map {jpeg frontback} transform {maptransformf} rotate 90*x } } translate -0.5*<1,0,1> } object {box {<0,-0.01,0>,<1,0,1>} texture { pigment { image_map {jpeg frontback} transform {maptransformb} rotate 90*x rotate 180*z translate x } } finish {ambient RM_ambient} translate -0.5*<1,0,1> } #if (tiletype != 0) box {<-0.4,0,-0.05>,<0.4,0.015,0.05> rotate (-45*tiletype*y) texture {pigment {rgb <1,1,1>}} } box {<-0.4,-0.015,-0.05>,<0.4,0,0.05> rotate (45*tiletype*y) texture {pigment {rgb <1,1,1>}} } #end #if (arrow != 0) object {RM_arrow} #end #if (tid >= 0) text {ttf "timrom.ttf" str(tid,1,0) 0.02, 0 pigment {rgb <1,1,1>} rotate 90*x scale y+0.35*(x+z) translate 0.02*y+0.2*x-0.12*z finish {ambient RM_ambient} } text {ttf "timrom.ttf" str(tid,1,0) 0.02, 0 pigment {rgb <0.5,0.5,1>} rotate 90*x scale y+0.35*(x+z) translate 0.02*y+0.2*x-0.12*z rotate 180*z finish {ambient RM_ambient} } #end scale finish { specular .5 roughness .1 ambient RM_ambient } } } #end #local tid = 0; #while (tid < RM_numtiles) #if (RM_highresolution) #declare RM_Tile[tid][RM_DecMatchbox] = RM_createtile (tid, "rm_matchbox.jpg") #declare RM_Tile[tid][RM_DecLingao] = RM_createtile (tid, "rm_lingao.jpg") #declare RM_Tile[tid][RM_DecDogs] = RM_createtile (tid, "rm_dogs.jpg") #declare RM_Tile[tid][RM_DecMatchboxFast] = RM_createtile_fast (tid, "rm_matchbox.jpg") #else #declare RM_Tile[tid][RM_DecMatchbox] = RM_createtile (tid, "rm_matchbox_lr.jpg") #declare RM_Tile[tid][RM_DecLingao] = RM_createtile (tid, "rm_lingao_lr.jpg") #declare RM_Tile[tid][RM_DecDogs] = RM_createtile (tid, "rm_dogs_lr.jpg") #declare RM_Tile[tid][RM_DecMatchboxFast] = RM_createtile_fast (tid, "rm_matchbox_lr.jpg") #end #declare RM_Tile[tid][RM_DecBlack] = RM_createtile_old ("rm_tileblack.jpg") #declare RM_Tile[tid][RM_DecTransparent] = object {RM_plastic} #local tid = tid + 1; #end #macro loadcustom (jpegfile) #local tid = 0; #while (tid < RM_numtiles) #declare RM_Tile[tid][RM_DecCustom] = RM_createtile (tid, jpegfile) #local tid = tid + 1; #end #end #local tid = 0; #while (tid < RM_numtiles) #declare RM_Tile[tid][RM_DecDiagonal] = RM_createtileg (-1, "rm_tileblack.jpg", 1, 0, -1) #declare RM_Tile[tid][RM_DecArrow] = RM_createtileg (-1, "rm_tileblack.jpg", 1, 1, -1) #declare RM_Tile[tid][RM_DecTileId] = RM_createtileg (-1, "rm_tileblack.jpg", 1, 1, tid) #local tid = tid + 1; #declare RM_Tile[tid][RM_DecDiagonal] = RM_createtileg (-1, "rm_tileblack.jpg", -1, 0, -1) #declare RM_Tile[tid][RM_DecArrow] = RM_createtileg (-1, "rm_tileblack.jpg", -1, 1, -1) #declare RM_Tile[tid][RM_DecTileId] = RM_createtileg (-1, "rm_tileblack.jpg", -1, 1, tid) #local tid = tid + 1; #end #macro RM_RMgen(numtiles, tileid, tiletype, moves, tr) #debug concat("moves: ",moves,"\n") #local mlen = strlen(moves); #local eat = 1; #local lift = 0; #while (mlen >= eat & asc(substr(moves,eat,1)) = 32) // skip spaces #local eat = eat + 1; #end #while (mlen >= eat & asc(substr(moves,eat,1)) = 43) // '+' #local eat = eat + 1; #local lift = lift + 1; #end #while (mlen >= eat & asc(substr(moves,eat,1)) = 45) // '-' #local eat = eat + 1; #local lift = lift - 1; #end #local liftamountind = 0; #if (lift != 0 & mlen >= eat) #local liftamountindc = asc(substr(moves,eat,1)) - 48; // value of decimal char #if (liftamountindc >= 0 & liftamountindc <= 9) #local eat = eat + 1; #local liftamountind = liftamountindc; #end #end #while (mlen >= eat & asc(substr(moves,eat,1)) = 32) //skip spaces #local eat = eat + 1; #end #local tid = tileid; #if (tid > 7) #local tid = tid - 8; #end #if (tid < 0) #local tid = tid + 8; #end #ifndef (RM_LiftAmounts[0]) #ifdef (RM_LiftAmount) #declare RM_LiftAmounts[0] = RM_LiftAmount; #end #end object { RM_Tile[tid][RM_Decoration] translate lift*RM_LiftAmounts[liftamountind]*y rotate RM_FlipTiles*180*z rotate tiletype*RM_TilesRotation*90*y transform {tr} } #local partialval = 1; #ifndef (RM_Partials[0]) #ifdef (RM_Partial) #declare RM_Partials[0] = RM_Partial; #end #end #if (numtiles > 1) #if (mlen >= eat) #local dir = asc(substr(moves,eat,1)); #local fold = 0; #if (mlen > eat) #local fld = asc(substr(moves,eat + 1,1)); #switch (fld) #case(109) // 'm' #local fold = 90; #local eat = eat + 1; #break; #case(118) // 'v' #local fold = -90; #local eat = eat + 1; #break; #end #if (mlen > eat) #local fld = asc(substr(moves,eat + 1,1)); #switch (fld) #case(109) // 'm' #local fold = fold + RM_secondfold; #local eat = eat + 1; #break; #case(118) // 'v' #local fold = fold - RM_secondfold; #local eat = eat + 1; #break; #end #end #if (mlen > eat) #local partial = asc(substr(moves,eat + 1,1)); #local partialfound = 0; #switch (partial) #case(112) // 'p' #local eat = eat + 1; #local partialfound = 1; #break #case(80) #local eat = eat + 1; #local partialfound = 1; #break #case(81) // 'Q' #local eat = eat + 1; #local partialfound = 1; #break #end #local partialind = 0; #if (fold != 0 & partialfound & mlen >= eat + 1) #local partialindc = asc(substr(moves,eat + 1,1)) - 48; // value of decimal char #if (partialindc >= 0 & partialindc <= 9) #local eat = eat + 1; #local partialind = partialindc; #end #end #switch (partial) #case(112) // 'p' #local partialval = RM_Partials[partialind]; #break #case(80) #local partialval = 1-RM_Partials[partialind]; #break #case(81) // 'Q' #local partialval = 2-RM_Partials[partialind]; #break #end #end #end #local nmoves = substr(moves, eat+1, mlen-eat); #local newtr = transform {translate RM_Thickness*<1,1,1>} #local fold = fold*partialval; #switch (dir) #case(82) // R #local newtr = transform {translate RM_HalfSize*x rotate -fold*z translate RM_HalfSize*x} #break #case(76) // L #local newtr = transform {translate -RM_HalfSize*x rotate fold*z translate -RM_HalfSize*x} #break #case(85) // U #local newtr = transform {rotate 180*y translate RM_HalfSize*z rotate fold*x translate RM_HalfSize*z} #break #case(68) // D #local newtr = transform {rotate 180*y translate -RM_HalfSize*z rotate -fold*x translate -RM_HalfSize*z} #break #end RM_RMgen(numtiles - 1, tid + RM_TilesIncrement, -tiletype, nmoves, transform{newtr tr}) #end #end #end #macro RM_RubiksMagic(moves) #ifndef (RM_StartTile) #declare RM_StartTile = 0; #end #ifndef (RM_StartType) #declare RM_StartType = 1; #end #ifndef (RM_TilesIncrement) #declare RM_TilesIncrement = 1; #end #ifndef (RM_TilesRotation) #declare RM_TilesRotation = 0; #end #ifndef (RM_FlipTiles) #declare RM_FlipTiles = 0; #end #ifndef (RM_Decoration) #declare RM_Decoration = RM_DecMatchbox; #end union { RM_RMgen(8, RM_StartTile, RM_StartType, moves, transform {rotate 0*x}) scale RM_Scale[RM_Decoration] } #end