Message Boards Message Boards


Anamorphic Reflection Magic on Floor & Ceiling

Posted 2 years ago
2 Replies
20 Total Likes

enter image description here

It looks hard to decipher the text in the above image : that's because it is a cylindrical anamorphic image. This means you need a cylindrical mirror and a specific viewpoint to interpret the image correctly. I made several Wolfram Demonstrations about this type of anamorphism in the past. This drew the attention of a Brazilian friend who sent me an article about a remarkable example of cylindrical anamorphosis in the São Paulo República metro station by Antonio Peticov. See the website: "Underground collection: art in São Paulo subway"

enter image description here

In my past demonstrations, the viewer has to look down on a deformed image on the floor to see the real image reflected. Here however, the viewer has to look upward to see the anamorphic image on the ceiling reflected as an undeformed image in the cylindrical mirror. This appeared to me like an unexpected extension of my previous contributions and I set myself to work with Mathematica to see if I could reproduce this type of anamorphic art. First the geometry:

enter image description here

A point S on the floor (left) or ceiling (right) will emit a light-ray SQ that reflects as QV to the viewer's eye at V. With the eye at V, a person looks downward (left) or upward (right) and sees a point S on the ceiling as a point I reflected in the cylindrical mirror. The points S, Q, V and I are all in the same plane and n is the normal to the cylinder at Q and the rays sQ and vQ will form equal angles with n. To make it compatible with both floor- and ceiling anamorphosis, I modified the function used in my demonstrations. The function has to detect if the viewer is looking up- or downward by comparing the z coordinates of the viewer (at V) and the image point (at I). The new function cylAnamorphMap performs 3 steps:

  1. Find Q, the intersection point of the view line VI and the cylinder (assumed to have unit radius)

  2. Find the symmetrical of the view line VQI to the normal n to cylinder at Q

  3. Find the intersection point S of the symmetric view line with the horizontal plane plane at z=0 (floor level) or z=h (ceiling height). This point S is the anamorphic map of the image point I.

    cylAnamorphMap[{xi_, zi_}, {yv_, zv_}, h_] := 
     Module[{ptV, ptI, ptQ, ptVv}, ptV = {0, yv, zv}; 
      ptI = {xi, 1/yv, zi}; 
      ptQ = {x, y, z} /. 
        First[NSolve[{Element[{x, y, z}, HalfLine[{ptI, ptV}]] && 
            x^2 + y^2 == 1}, {x, y, z}]]; 
      ptVv = ReflectionTransform[ReplacePart[ptQ, 3 -> 0], ptQ][ptV]; {x, 
        y, z} /. First[
        NSolve[{Element[{x, y, z}, HalfLine[{ptVv, ptQ}]] && 
           z == If[zv > zi, 0, h]}, {x, y, z}]]]

The following GIF's show the function in action on a point moving around a circle and around its anamorphic map on the floor (left) or the ceiling (right)

enter image description here

To test our function in practice, we first try an experiment with a text message. The following code scales and centers the text and converts it into a FilleCurve object:

loveCeiling[h0_] := 
        Style["\[Star] What the\n    \[HeartSuit]   World \[Star]\n\
\[Star]needs now", FontFamily -> "American Typewriter", 
         FontWeight -> "SemiBold", LineSpacing -> {0, 12}], "PDF"], 
       "TextMode" -> "Outlines"]]] /. {x_?NumericQ, 
      y_?NumericQ} :> {x, y} .033 /. {x_?NumericQ, 
     y_?NumericQ} :> {x - .92, y + .9 + h0};
loveFloor[h0_] := 
        Style["\n    \[HeartSuit]  is Love \[Star]\n    \
\[Star]\[Star] sweet \[Star]\[Star]\n    \[HeartSuit]\[HeartSuit] \
Love \[HeartSuit]\[HeartSuit]", FontFamily -> "American Typewriter", 
         FontWeight -> "SemiBold", LineSpacing -> {0, 12}], "PDF"], 
       "TextMode" -> "Outlines"]]] /. {x_?NumericQ, 
      y_?NumericQ} :> {x, y} .036 /. {x_?NumericQ, 
     y_?NumericQ} :> {x - 1.21, y - .05 + h0};
love[h0_] := Join[loveFloor[h0], loveCeiling[h0]];
Graphics[love[.5], Axes -> True, AxesOrigin -> {-1., 0.35}, 
 ImageSize -> Small]

enter image description here

We now use the function cylAnamorphMap to create the anamorphic map printout for the floor (left) or the ceiling (right):

Module[{ptsFloor, ptsCeiling, anaPtsFloor, 
  anaPtsCeiling}, {ptsFloor, ptsCeiling} = 
  DeleteCases[love[.05], Thickness[_], \[Infinity]] /. 
      FilledCurve[_, pts_] :> pts /. {x_?NumericQ, y_} :> # & /@ {{-x,
      y}, {x, -(y - 1.) + 1.}};
 {anaPtsFloor, anaPtsCeiling} = 
     Most[cylAnamorphMap[#1, {10., 7}, 10]] &, #, {5}] & /@ {ptsFloor,
  Graphics[{Circle[], Map[Line, #, {3}]}] & /@ {anaPtsFloor, 

enter image description here

We put a cylindrical mirror on top of the two printouts and look at the reflection: this is a photograph of the reflection of the floor image looking downward in the mirror (left) and the reflection of the ceiling image looking upward (right).

enter image description here

Next we take a photographic image. Since our function cylAnamorphMap operates on 2-dimensional coordinates, we have to convert the image information into an array of coordinates (squares) and their corresponding colors. The following code will divide a photo into an array of colored squares.

square[center : {x_, 
    y_}] :=(*gives the coordinates of a unit square centered around \
{x,y}*)With[{s = .5}, {{x - s, y - s}, {x - s, y + s}, {x + s, 
    y + s}, {x + s, y - s}}]
convert2Squares[image_] :=
 Module[{xi, yi, centers, densities},
  {xi, yi} = ImageDimensions[image];
  centers = ParallelTable[{x, y}, {x, .5, xi}, {y, 0.5, yi}];
  densities = 
   ParallelMap[RGBColor@ImageValue[image, #] &, centers, {2}];
  MapThread[{EdgeForm[#2], FaceForm[#2], 
     Polygon[square[#]]} &, {centers, densities}, 2]]
img = ImageResize[ExampleData[{"TestImage", "Mandrill"}], 200];
convert2Squares[ImageResize[img, 25]] // Short[#, 2] &
Graphics[%, Axes -> True]

enter image description here

This code will scale and center the array of squares to fit within the cylinder.

centerScaleLift[sqrs_List, scl_, dzi_ : 0] :=

 Module[{xi}, xi = First@Dimensions[sqrs];
  sqrs /. {x_?NumericQ, y_?NumericQ} :> 
    2 scl*{x - .5 xi, y *dzi/scl}/xi]

Th function anamorphSquareTiles will divide the photographic image into an array of squares created by convert2Squares and will convert the vertices of the squares into their anamorphic map using cylAnamorphMap.

anamorphSquareTiles[image_, viewdPoint : {xv_, zv_}, scale_ : .95, 
  dzi_ : 1] :=
  polySpecs = centerScaleLift[convert2Squares[image], scale, dzi];
  MapAt[Most[cylAnamorphMap[#, {xv, zv}, 10]] &, 
   polySpecs, {All, All, -1, All, All}]]

This is the result of the above function applied to the image img, creating an anamorphic version for reflection from the floor (left) and from the ceiling (right)

       ImageReflect[img, Left -> Right], {100, 70}]}], \[Pi]]
      anamorphSquareTiles[ImageReflect@img, {100, 70}]}], \[Pi]]}]

enter image description here

These are photos of the above printouts reflected in the cylindrical mirror. Looking down (left) or upward into the mirror (right).

enter image description here

Here is how I proceeded to make these photographs: The mirror core is a cylindrical cardboard sugar shaker: diameter 85mm and height 185mm. The reflective material is rolled several times around the cylinder: "heat reflecting window film". The paper printouts were the Mathematica outputs saved as .pdf files and printed as a 2 page "Poster" with Adobe Acrobat .

enter image description here

In case you can not make your own mirror, you can still explore anamorphism in the virtual world. Here is a simulation of the reflection of both the floor (left) and ceiling (right) anamorphic text.

enter image description here

Imagine a large room with the previous anamorphic images painted on floor and ceiling and the message correctly reflected in the mirror column to be admired by the entering visitor! You can enter this room with the code in the attached notebook "Mathematica floor and ceiling.nb"

enter image description here

Hope you enjoyed the magic of anamorphic reflection!

POSTED BY: Erik Mahieu
2 Replies

enter image description here -- you have earned Featured Contributor Badge enter image description here Your exceptional post has been selected for our editorial column Staff Picks and Your Profile is now distinguished by a Featured Contributor Badge and is displayed on the Featured Contributor Board. Thank you!

POSTED BY: Moderation Team

Hi Erik,

I'm from Sao Paulo, Brazil, and I'm impressed with your codes, very good! :) ...really your work is more than amazing! Thank you for mentioning my city and for your work that you have developed so far!!

Claudio Chaib

POSTED BY: Claudio Chaib
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
or Discard

Group Abstract Group Abstract