{"id":232,"date":"2011-11-12T12:16:27","date_gmt":"2011-11-12T20:16:27","guid":{"rendered":"http:\/\/new.joyofprocessing.com\/blog\/?p=232"},"modified":"2011-11-12T15:34:33","modified_gmt":"2011-11-12T23:34:33","slug":"number-zoom","status":"publish","type":"post","link":"https:\/\/joyofprocessing.com\/blog\/2011\/11\/number-zoom\/","title":{"rendered":"Number Zoom"},"content":{"rendered":"<p><script type=\"application\/processing\">\/\/ Number Zoom - Continuous cross-fading zoom effect by Jim Bumgardner (joyofprocessing.com)\n\nPImage tex;\n\nfloat  duration = 5*1000;\n\nfloat scalarWrap(float v) \/\/ Wrap texture coordinates that exceed 0-1 range in either direction.\n{\n  if (v < 0) {\n    v = -v;\n    v = 1-(v-(int) v);\n  } else {\n    v -= (int) v;\n  }\n  return v;\n}\n\nvoid setup()\n{\n  \/* @pjs preload=\"\/1010.png\"; *\/\n\n  size(500,500);\n  \/\/ size(600,600, OPENGL);     \/\/ Use OPENGL if you can...\n  \/\/ hint(ENABLE_OPENGL_4X_SMOOTH);\n  \/\/ hint(DISABLE_OPENGL_ERROR_REPORT);\n  \n  smooth();\n  tex = loadImage(\"\/1010.png\");\n  tex.loadPixels();\n}\n\nvoid draw()\n{\n  background(255);\n  noStroke();\n\n  loadPixels();\n  float r = millis()\/duration;\n  r -= (int) r;\n\n  float r2 = (millis()+duration\/2)\/duration;\n  r2 -= (int) r2;\n  \n  float startWidth = 1;                               \/\/ Beginning width of first layer in texture-space\n  float endWidth = tex.width;                         \/\/ Ending width of first layer in texture-space\n  float z = endWidth\/startWidth;                      \/\/ Zoom ratio\n\n  int fullImage = width*height;\n\n  float curWidth = startWidth*pow(z, r);              \/\/ Determine current width in texture-space (0-1)\n\n  PImage t1 = tex;\n  \n  PImage t2 = tex;\n\n  for (int i = fullImage-1; i >= 0; --i) {\n    int px = (i % width) - (width\/2);                 \/\/ Pixel Coordinates, offset so we zoom from center\n    int py = (i \/ width) - (height\/2);\n\n    float px1 = px*curWidth\/width;                    \/\/ Texture Coordinates (0-1 across texture)\n    float py1 = py*curWidth\/width;\n\n    float fx1 = scalarWrap(px1);                      \/\/ Wrapped Texture Coordinates\n    float fy1 = scalarWrap(py1);\n\n    int dx1 = (int) (fx1*t1.width) % t1.width;        \/\/ Texture pixel coordinates\n    int dy1 = (int) (fy1*t1.height) % t1.height;\n\n    float v1 = t1.pixels[dy1*t1.width + dx1] & 0x0FF; \/\/ Texture color at this location\n\n\n    float fx2 = scalarWrap((.5 + px1\/t2.width));      \/\/ Wrapped Texture Coords for second layer\n    float fy2 = scalarWrap((.5 + py1\/t2.width));\n    int dx2 = (int) (fx2*t2.width) % t2.width;        \/\/ Texture Pixel Coords for second layer\n    int dy2 = (int) (fy2*t2.height) % t2.height;\n\n    float v2 = t2.pixels[dy2*t2.width + dx2] & 0x0FF; \/\/ Texture color at this location\n\n    float ra = r*r*(3-2*r);                           \/\/ Alpha cross-fade with Ease In\/Out\n\n    int v = (int) ((v2 * ra) + (v1 * (1-ra)));        \/\/ Tween between color values\n\n    pixels[i] = v | (v << 8) | (v << 16) | 0xFF000000; \/\/ Set pixel value\n  }\n\n  updatePixels();\n}\n\/\/ DONE\n\n\n<\/script><\/p>\n<div class=\"ps_cap\"><a href=\"\/showexample.php?ex=number_zoom\">source<\/a><\/div>\n<p>This advanced sketch cross fades between two continuously zooming copies of the same image.  The first one starts at 100% opacity, showing the image filling the frame of the window, and as it zooms out revealing little tiny copies of itself, it fades out.<\/p>\n<p>The second layer starts at 0% opacity, with a gigantic zoomed-in version of the image.  As it zooms out to where the image fills the frame, the opacity goes to 100%, matching the beginning state of the first layer.<\/p>\n<p>The zooms are matched so that the large images appear to be constructed from the small images, but this is just an illusion.  This illusion is heightened by making the source image contain little copies of itself:<\/p>\n<div class=\"exc\"><img decoding=\"async\" src=\"\/1010.png\" width=256 height=256 \/><\/div>\n<p>This sketch is advanced, not because it is long (it isn&#8217;t that long), but because some of the lines require very specific ratios, in order to get things to line up correctly.  It took a bit of trial and error to get the math right.  A long sketch isn&#8217;t necessarily an advanced sketch (and it&#8217;s not uncommon for novices to write longer sketches than they need to).<\/p>\n<p>This kind of sketch would be significantly easier to make (and look nicer) if Processing supported OPENGL&#8217;s texture wrap modes, which supports repeating textures across a surface.  Since it doesn&#8217;t, I basically had to construct a bare-bones texture mapper that supports this feature.<\/p>\n<p>Sketches like this one have an important property that leads to the perception of wonderment: your brain thinks it almost understands what is going on, but not quite.  The sensation that you are about to figure out, followed by not actually figuring it out, creates a delightful sensation of wonder.  This is a quality I often chase after in my advanced sketches.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>source This advanced sketch cross fades between two continuously zooming copies of the same image. The first one starts at 100% opacity, showing the image filling the frame of the window, and as it zooms out revealing little tiny copies of itself, it fades out. The second layer starts at 0% opacity, with a gigantic [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[21,22],"class_list":["post-232","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-advanced","tag-zoom"],"_links":{"self":[{"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/posts\/232","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/comments?post=232"}],"version-history":[{"count":7,"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/posts\/232\/revisions"}],"predecessor-version":[{"id":238,"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/posts\/232\/revisions\/238"}],"wp:attachment":[{"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/media?parent=232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/categories?post=232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/joyofprocessing.com\/blog\/wp-json\/wp\/v2\/tags?post=232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}