/** * CSC480Spring2020QuiltAnimate adapted from QuiltAnimate in March 2020 * as a demo for CSC480 potential project extensions. D. Parson * * QuiltAnimate derived from Quilter and QuiltArchMontage for interactive * tessellation, D. Parson, February 2019. * Sketch Quilter based on patterns in chapters 3 & 4 of * *Designing Tesselations* by Jinney Beyer, D. Parson, February 2019. * QuiltAnimate loads PImages from files in loadNextCell() * *'p*\n' and 'c*\n' change tiling pattern, use 'p*.n' or 'c*.n' for n variants p1 variants: 1 (reference) p2 variants: 3 p4 variants: 2 (windmills with lecture hall, boats) pg variants: 3 pm variants: 4 (evens OK for all, odds not good for irregulars) cm variants: 2 (horizontal, vertical OK) pmg variants: 2 (horizontal, vertical OK) p4g variants: 4 (good for large blocks) p4m variants: 1 (windmill) px variants: 12 (good for lib classroom & sun window) *'a' advances to next PImage *'A' shreds current PImage, uncovering next PImage *'sNNNN\n' changes number of pixels per tile (scale) *'F' toggles freeze-display *'b' sets re-painting background() to false *'B' sets re-painting background() to true *LEFT and RIGHT arrow update horizontal row motions *UP and DOWN arrow update vertical column motions *'~' toggles sin wave up-down translation cycle *'t' toggles printing *'P' toggles frame plotting for Movie Maker *'NN\n' sets the degree of tile nesting, [1, 8] *'rN\n' sets the degree of image recursion *'hN\n' sets horizontal pixel shearing, N < 0 goes right, N > 0 goes left, starting from top *'HN\n' sets horizontal pixel shearing, N < 0 goes right, N > 0 goes left, starting from bottom *'vN\n' sets vertical pixel shearing, N < 0 goes down, N > 0 goes up, starting from left *'VN\n' sets vertical pixel shearing, N < 0 goes down, N > 0 goes up, starting from right *'iN\n' sets horizontal sine wave modulation, N > 0 to right, N < 0 to left, N==0 stops, N sets sine amplitude in N steps *'IN\n' sets vertical sine wave modulation, N > 0 down, N < 0 up, N==0 stops, N sets sine amplitude in N steps * For 'i' or 'I", N can be an int or int.frac, where frac between 0 and .99* determines random deviation from sine wave *'N\n' sets pixel rate for horizontal scrolling *'^N\n' sets pixel rate for vertical scrolling *'TN\n' sets the tint() sequence * X, Y and R are deprecated as ineffective: *'XNNN\n' and 'YNNN\n' set shearing radians, use NN=10 to stop it, *experimental* *'RNNN\n' sets rotation degrees, use NN=1000, *experimental* **/ import java.util.* ; import java.util.concurrent.* ; import java.io.File ; final String imagedir = "CSC480QuiltImages"; float scalesize = 300 ; // 1950.0 ; // use this as reference size in cell construction boolean isFrozen = false ; boolean isPrinting = true ; // 't' flips this, print transform to the display boolean isPlotting = false ; // 'P' flips this, plot display to a png file boolean isBackground = false ; PImage hexagonMask = null ; final PImageLoader imgloader = new PImageLoader(); final Thread imgloaderThread = new Thread(imgloader); final float translateRate = 1 ; // pixels per transition in StatefulTiles final float rotateRate = TWO_PI / 360.0 ; // radians per transition in StatefulTiles final float scaleRate = .01 ; // vary from -1 to 1 for scalex and scaley // Interaction variables Tiler [] tilers = null ; HashMap nextTileCommand2Index = new HashMap(); String nextTileCommand = "p1"; int nextTileIndex = 0 ; int currentTileVariant = 0, nextTileVariant = 0 ; Tiler currentTiler = null, nextTiler = null ; boolean isStableTiler = true ; List currentDisplayList = null ; List nextDisplayList = null ; StatefulTile [][] stableDisplayArray = null ; // [col][row] int maxcol = 0, maxrow = 0 ; // Current stableDisplayArray, extracted from *Display list. float toscalesize = scalesize ; // growth/shinkage takes place only when stable boolean isGrowingShrinking = false ; PImage currentPImage = null ; // Added 2/25/2019 for some motion of all images boolean intransit = false ; boolean indecay = false ; int verticalTransit = 0 ; int horizontalTransit = 0 ; boolean verticalAbating = false, horizontalAbating = false, transitAbating = false ; boolean sinTransit = false, sinAbating = false ; ; float sinAngle = 0 ; /* SKIP SHEARING AND IMAGE ROTATION - NOT EFFECTIVE float shearxAngle = 0, shearxAngleSpeed = 0, roterAngle = 0, roterAngleSpeed = 0 ; float shearyAngle = 0, shearyAngleSpeed = 0 ; boolean shearXabating = false, shearYabating = false, roterAbating = false ; */ boolean shredding = false ; final int shreadSpeed = 2 ; int shreadCountdown = 0 ; int [][] tints = { {0}, {0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF00FFFF, 0xFFFFFF00} }; int tintsIndex = 0, tintsOffset = 0 ; int nestLevel = 0 ; // used to nest cells around their shared center when > 1 int toRecursion = 0, atRecursion = 0 ; // added 3/15/2019 for recursive screen shots int hSlideRate = 0, hSlideProgress = 0 ; int vSlideRate = 0, vSlideProgress = 0 ; boolean hSlideInvert = false, vSlideInvert = false ; // Added 3/26/2016 for horizontal & vertical slide morphing. boolean horizontalSineGo = false, horizontalSineInvert = false ; int horizontalSineToGo = 0 ; boolean verticalSineGo = false, verticalSineInvert = false ; int verticalSineToGo = 0 ; int [] hSineWaveSteps = null, vSineWaveSteps = null ; float hSineDeviation = 0.0, vSineDeviation = 0.0 ; int [] hSineWaveProgress = null ; int [] vSineWaveProgress = null ; int verticalScrollSpeed = 0, verticalScrollLocation = 0, priorVerticalScrollSpeed = 0 ; int horizontalScrollSpeed = 0, horizontalScrollLocation = 0, priorHorizontalScrollSpeed = 0 ; // Helper for the 'A' command void doShread() { if (shredding && shreadCountdown > 0) { shreadCountdown-- ; return ; } if (nextDisplayList != null) { for (StatefulTile atile : nextDisplayList) { atile.display(); } } else { shredding = false ; return ; } if (shreadCountdown <= 0) { shreadCountdown = shreadSpeed ; } if (currentDisplayList.size() == 0) { currentTiler = null ; // force a redraw shredding = false ; } else { int killer = int(random(currentDisplayList.size())); currentDisplayList.remove(killer); if (currentDisplayList.size() == 0) { currentTiler = null ; // force a redraw shredding = false ; } else { for (StatefulTile atile : currentDisplayList) { atile.display(); } } } } // Helper for LEFT-RIGHT-UP-DOWN-SIN cycling and decay (abating). void processTransits(List displayList) { // boolean impact = false ; final int sinMultiplier = 500 ; float gains = 0 ; if (verticalTransit == 0 && horizontalTransit == 0 && !(verticalAbating || horizontalAbating|| /* roterAbating || */ sinTransit || sinAbating || indecay)) { intransit = false ; // isStableTiler = true ; return ; } if (sinTransit) { float s = sin(sinAngle) ; gains += s * sinMultiplier ; // println("DEBUG gains " + gains); sinAngle += TWO_PI / 360.0 ; if (sinAngle > TWO_PI) { sinAngle = 0 ; if (sinAbating) { sinAbating = false ; sinTransit = false ; } } } boolean quitting = (verticalTransit == 0 && horizontalTransit == 0 && ! sinTransit); indecay = false ; if (gains != 0) { translate(0, gains); } tintsOffset = 0 ; // always start at the beginning in .display() to avoid flicker for (StatefulTile atile : displayList) { if (quitting) { boolean moved = atile.advance() ; // send it home indecay = indecay || moved ; } else { atile.x += ((atile.row & 1) == 0) ? horizontalTransit : - horizontalTransit ; atile.x = (atile.x + width + atile.cell.width/2) % (width + atile.cell.width/2) ; // atile.y += gains ; atile.y += ((atile.col & 1) == 0) ? verticalTransit : - verticalTransit ; atile.y = (atile.y + height + atile.cell.height/2) % (height + atile.cell.height/2) ; indecay = indecay || atile.x != atile.tox || atile.y != atile.toy ; } atile.display(); } if (quitting) { verticalAbating = horizontalAbating = sinTransit = sinAbating = false ; sinAngle = 0 ; } intransit = indecay || ! quitting ; if (isStableTiler && ! intransit) { currentTiler = null ; // force a redraw } } // Pull image in (optional) and construct graphics. // loadPImage is false when changing only scale (sNN\n) void loadNextCell(boolean loadPImage) { /* DEBUG if (isPlotting && imgloader.getDisplayedImageIndex() == imgFileNames.size()-1) { exit(); // DEBUG USED ONLY WHEN PLOTTING FULL SET } */ if (loadPImage) { currentPImage = null ; } // PImage img = loadImage(imagedir + "/" + imgFileNames.get(imgFileIndex)); while (currentPImage == null) { try { currentPImage = imgq.take(); } catch (InterruptedException x) { } } // loadPImage will be false on a change to scalesize Cell cell = new Cell(currentPImage, round(scalesize), round(scalesize)); tilers = new Tiler[10]; // bump to 10 with the hexagons tilers[0] = new TranslationSymmetry_p1(cell); tilers[1] = new HalfTurnRotationSymmetry_p2(cell); tilers[2] = new QuarterTurnRotationSymmetry_p4(cell); tilers[3] = new GlideSymmetry_pg(cell); tilers[4] = new MirrorSymmetry_pm(cell); tilers[5] = new StaggeredMirrorSymmetry_cm(cell); tilers[6] = new GlidedStaggeredMirrorSymmetry_pmg(cell); tilers[7] = new FourRotationsMirrorSymmetry_p4g(cell); tilers[8] = new TraditionalBlockSymmetry_p4m(cell); nextTileCommand2Index.clear(); nextTileCommand2Index.put("p1", 0); nextTileCommand2Index.put("p2", 1); nextTileCommand2Index.put("p4", 2); nextTileCommand2Index.put("pg", 3); nextTileCommand2Index.put("pm", 4); nextTileCommand2Index.put("cm", 5); nextTileCommand2Index.put("pmg", 6); nextTileCommand2Index.put("p4g", 7); nextTileCommand2Index.put("p4m", 8); PImage hexxed = currentPImage.copy(); hexxed.resize(hexagonMask.width, hexagonMask.height); hexxed.mask(hexagonMask); Cell hexcell = new Cell(hexxed, round( scalesize*hexagonMask.width/hexagonMask.height), round(scalesize)); tilers[9] = new HexSymmetry_px(hexcell); nextTileCommand2Index.put("px", 9); // printVariants(); } // Helper for bottom of screen assists. void printVariants() { println("p1 variants: " + tilers[0].getNumberOfVariants()); println("p2 variants: " + tilers[1].getNumberOfVariants()); println("p4 variants: " + tilers[2].getNumberOfVariants()); println("pg variants: " + tilers[3].getNumberOfVariants()); println("pm variants: " + tilers[4].getNumberOfVariants()); println("cm variants: " + tilers[5].getNumberOfVariants()); println("pmg variants: " + tilers[6].getNumberOfVariants()); println("p4g variants: " + tilers[7].getNumberOfVariants()); println("p4m variants: " + tilers[8].getNumberOfVariants()); println("px variants: " + tilers[9].getNumberOfVariants()); println("verticalTransit = " + verticalTransit + ", verticalAbating = " + verticalAbating); println("horizontalTransit = " + horizontalTransit + ", horizontalAbating = " + horizontalAbating); println("sinTransit = " + sinTransit + ", sinAbating = " + sinAbating); } void setup() { // fullScreen(P2D); size(1200,1040,P2D); // Don't go fullScreen with Zoom frameRate(60); background(0); shapeMode(CENTER); imageMode(CENTER); hexagonMask = loadImage("HexMask_1200_1040.jpg"); String path = sketchPath(imagedir); println("SP: " + path); File curdir = new File(path); String [] filenames = curdir.list(); java.util.Arrays.sort(filenames); // make order predictable LinkedList ifiles = new LinkedList(); for (String f : filenames) { if (f.length() > 4 && (f.charAt(0) != '.') && (f.endsWith(".JPG") || f.endsWith(".jpg") || f.endsWith(".PNG") || f.endsWith(".png"))) { print(f + " "); ifiles.add(f); } } imgFileNames = new CopyOnWriteArrayList(ifiles); imgloaderThread.start(); loadNextCell(true); } void doGrowShrink() { boolean changed = false ; if (toscalesize < scalesize) { scalesize -= 8 ; if (scalesize < toscalesize) { scalesize = toscalesize ; } changed = true ; } else if (toscalesize > scalesize) { scalesize += 8 ; if (scalesize > toscalesize) { scalesize = toscalesize ; } changed = true ; } if (! changed) { isGrowingShrinking = false ; currentTiler = null ; // force redraw } else { loadNextCell(false); // stay with current image, but use updated scalesize currentTiler = nextTiler = tilers[nextTileIndex] ; currentTileVariant = nextTileVariant ; currentDisplayList = nextDisplayList = currentTiler.tileDisplay(nextTileVariant, true); isStableTiler = true ; extractstableDisplayArray(currentDisplayList); } } void draw() { if (isFrozen) { return ; // used for debugging } if (isBackground) { background(0); //255,255,0); if (isStableTiler && ! shredding) { currentTiler = null ; // force redraw } } pushMatrix(); // SKIP processRotateShear(); if (shredding) { // Suspend other types of display processing until 'A' is completed. doShread(); } else if (isGrowingShrinking) { doGrowShrink(); } else if (currentTiler == null) { // currentTiler = null is used throughout to force a stable redraw currentTiler = nextTiler = tilers[nextTileIndex] ; currentTileVariant = nextTileVariant ; currentDisplayList = nextDisplayList = currentTiler.tileDisplay(nextTileVariant, true); isStableTiler = true ; extractstableDisplayArray(currentDisplayList); } else if ((!isStableTiler) || currentTiler != nextTiler || currentTileVariant != nextTileVariant) { // Display is transitioning from one tessellation to another. boolean madeUpdate = false, tmpupdate = false ; tintsOffset = 0 ; // always start at the beginning in .display() to avoid flicker for (StatefulTile atile : currentDisplayList) { tmpupdate = atile.advance(); atile.display(); madeUpdate = madeUpdate || tmpupdate ; // order matters here! } isStableTiler = ! madeUpdate ; if (isStableTiler) { currentTiler = nextTiler = tilers[nextTileIndex] ; currentTileVariant = nextTileVariant ; // redraw in stable position to fix any rounding error currentDisplayList = nextDisplayList = currentTiler.tileDisplay(nextTileVariant, true); extractstableDisplayArray(currentDisplayList); } } // Is there any UP DOWN LEFT RIGHT ~ motion in progress or abating? processTransits(currentDisplayList); popMatrix(); /* if (isPlotting) { saveFrame("movies/#####_" + nextTileCommand + "_" + nextTileIndex + ".png"); // isPlotting = false ; // DEBUG } */ processPixelMorphing(); } void processPixelMorphing() { if (atRecursion > toRecursion) { atRecursion = 0 ; } if (atRecursion < toRecursion) { PImage recur = createImage(width, height, ARGB); loadPixels(); recur.loadPixels(); System.arraycopy(pixels, 0, recur.pixels, 0, pixels.length); recur.updatePixels(); currentPImage = recur ; atRecursion++ ; loadNextCell(false); advanceAfterLoadNextCell(); } else { atRecursion = toRecursion = 0 ; if (currentPImage != null && (hSlideRate != 0 || vSlideRate != 0)) { PImage tmpimg = currentPImage.copy(); tmpimg.resize(round(scalesize), round(scalesize)); PImage recur = createImage(tmpimg.width, tmpimg.height, ARGB); tmpimg.loadPixels(); recur.loadPixels(); if (hSlideRate != 0) { while (hSlideRate < 0) { // mod arithmetic hSlideRate += tmpimg.width ; } hSlideRate = hSlideRate % tmpimg.width ; int ystart = 0, yincr = 0 ; if (hSlideProgress < 0) { hSlideProgress = hSlideInvert ? tmpimg.height - 1 : 0 ; } if (hSlideInvert) { ystart = tmpimg.height - 1 ; yincr = -1 ; } else { ystart = 0 ; yincr = 1 ; } for (int ylim = ystart ; (((!hSlideInvert) && ylim <= hSlideProgress && ylim < tmpimg.height) || (hSlideInvert && ylim >= hSlideProgress && ylim >= 0)) ; ylim += yincr) { int to0 = hSlideRate ; int count0 = tmpimg.width - hSlideRate ; System.arraycopy(tmpimg.pixels, 0+ylim*tmpimg.width, recur.pixels, to0+ylim*tmpimg.width, count0); System.arraycopy(tmpimg.pixels, count0+ylim*tmpimg.width, recur.pixels, 0+ylim*tmpimg.width, tmpimg.width-count0); } for (int ylim = hSlideProgress+yincr ; ylim < tmpimg.height && ylim >= 0 ; ylim += yincr) { System.arraycopy(tmpimg.pixels, 0+ylim*tmpimg.width, recur.pixels, 0+ylim*tmpimg.width, tmpimg.width); } if (((!hSlideInvert) && hSlideProgress < tmpimg.height) || (hSlideInvert && hSlideProgress > -1)) { hSlideProgress += yincr ; } if (hSlideProgress >= tmpimg.height || hSlideProgress < 0) { hSlideRate = hSlideProgress = 0 ; hSlideInvert = false ; } } else { // Get recur ready for a vertical translation. System.arraycopy(tmpimg.pixels, 0, recur.pixels, 0, tmpimg.pixels.length); } if (vSlideRate != 0) { recur.updatePixels(); tmpimg = recur.copy(); tmpimg.loadPixels(); recur.loadPixels(); while (vSlideRate < 0) { // mod arithmetic vSlideRate += tmpimg.height ; } vSlideRate = vSlideRate % tmpimg.height ; int xstart = 0, xincr = 0 ; if (vSlideProgress < 0) { vSlideProgress = vSlideInvert ? tmpimg.width - 1 : 0 ; } if (vSlideInvert) { xstart = tmpimg.width - 1 ; xincr = -1 ; } else { xstart = 0 ; xincr = 1 ; } for (int column = xstart ; (((!vSlideInvert) && column < tmpimg.width && column <= vSlideProgress) ||(vSlideInvert && column >= 0 && column >= vSlideProgress)) ; column += xincr) { for (int row = 0 ; row < tmpimg.height ; row++) { int torow = (row + vSlideRate) % tmpimg.height ; recur.pixels[torow*tmpimg.width+column] = tmpimg.pixels[row*tmpimg.width+column]; } } for (int column = vSlideProgress+xincr ; column < tmpimg.width && column >= 0 ; column += xincr) { for (int row = 0 ; row < tmpimg.height ; row++) { recur.pixels[row*tmpimg.width+column] = tmpimg.pixels[row*tmpimg.width+column]; } } if (((!vSlideInvert) && vSlideProgress < tmpimg.width) || (vSlideInvert && vSlideProgress >= 0)) { vSlideProgress += xincr ; } if (vSlideProgress >= tmpimg.width || vSlideProgress < 0) { vSlideRate = vSlideProgress = 0 ; vSlideInvert = false ; } } recur.updatePixels(); currentPImage = recur ; loadNextCell(false); advanceAfterLoadNextCell(); } else if (currentPImage != null && (horizontalSineGo || verticalSineGo)) { PImage tmpimg = currentPImage.copy(); tmpimg.resize(round(scalesize), round(scalesize)); PImage recur = createImage(tmpimg.width, tmpimg.height, ARGB); tmpimg.loadPixels(); recur.loadPixels(); if (hSineWaveSteps == null || hSineWaveSteps.length != tmpimg.width || vSineWaveSteps == null || vSineWaveSteps.length != tmpimg.width) { hSineWaveSteps = new int [ tmpimg.width ] ; vSineWaveSteps = new int [ tmpimg.width ] ; hSineWaveProgress = new int [ tmpimg.width ] ; // all zeroes, else zeroed in "i" command vSineWaveProgress = new int [ tmpimg.width ] ; float increment = TWO_PI / tmpimg.width ; float angle = 0 ; int mid = tmpimg.width / 2 ; for (int ix = 0 ; ix < hSineWaveSteps.length ; ix++) { int offset = (round(sin(angle) * mid) + tmpimg.width) % tmpimg.width ; hSineWaveSteps[ix] = round(offset + random(-1.0, 1.001) * hSineDeviation * offset) ; vSineWaveSteps[ix] = round(offset + random(-1.0, 1.001) * vSineDeviation * offset) ; angle += increment ; } } if (horizontalSineGo) { boolean isChange = false ; for (int ylim = 0 ; ylim < tmpimg.height ; ylim++) { if (hSineWaveProgress[ylim] >= hSineWaveSteps[ylim]) { System.arraycopy(tmpimg.pixels, 0+ylim*tmpimg.width, recur.pixels, 0+ylim*tmpimg.width, tmpimg.width); } else { hSineWaveProgress[ylim] += 1 ; isChange = true ; if (! horizontalSineInvert) { System.arraycopy(tmpimg.pixels, 0+ylim*tmpimg.width, recur.pixels, 1+ylim*tmpimg.width, tmpimg.width-1); recur.pixels[0+ylim*tmpimg.width] = tmpimg.pixels[(tmpimg.width-1)+ylim*tmpimg.width]; } else { System.arraycopy(tmpimg.pixels, 1+ylim*tmpimg.width, recur.pixels, 0+ylim*tmpimg.width, tmpimg.width-1); recur.pixels[(tmpimg.width-1)+ylim*tmpimg.width] = tmpimg.pixels[0+ylim*tmpimg.width]; } } } if (! isChange) { // We may have to do it again, depending on count. horizontalSineToGo-- ; if (horizontalSineToGo > 0) { Arrays.fill(hSineWaveProgress, 0); } else { horizontalSineGo = false ; } } } else { // Get recur ready for a vertical sine translation. System.arraycopy(tmpimg.pixels, 0, recur.pixels, 0, tmpimg.pixels.length); } if (verticalSineGo) { recur.updatePixels(); tmpimg = recur.copy(); tmpimg.loadPixels(); recur.loadPixels(); boolean isChange = false ; for (int xlim = 0 ; xlim < tmpimg.width ; xlim++) { if (vSineWaveProgress[xlim] >= vSineWaveSteps[xlim]) { for (int ylim = 0 ; ylim < tmpimg.height ; ylim++) { recur.pixels[ylim*tmpimg.width+xlim] = tmpimg.pixels[ylim*tmpimg.width+xlim]; } } else { vSineWaveProgress[xlim] += 1 ; isChange = true ; if (! verticalSineInvert) { for (int ylim = 0 ; ylim < (tmpimg.height-1) ; ylim++) { recur.pixels[(ylim+1)*tmpimg.width+xlim] = tmpimg.pixels[ylim*tmpimg.width+xlim]; } recur.pixels[xlim] = tmpimg.pixels[(tmpimg.height-1)*tmpimg.width+xlim]; } else { for (int ylim = 0 ; ylim < (tmpimg.height-1) ; ylim++) { recur.pixels[ylim*tmpimg.width+xlim] = tmpimg.pixels[(ylim+1)*tmpimg.width+xlim]; } recur.pixels[(tmpimg.height-1)*tmpimg.width+xlim] = tmpimg.pixels[xlim]; } } } if (! isChange) { // We may have to do it again, depending on count. verticalSineToGo-- ; if (verticalSineToGo > 0) { Arrays.fill(vSineWaveProgress, 0); } else { verticalSineGo = false ; } } } recur.updatePixels(); currentPImage = recur ; loadNextCell(false); advanceAfterLoadNextCell(); } } if (verticalScrollSpeed != 0 || priorVerticalScrollSpeed != 0 || horizontalScrollSpeed != 0 || priorHorizontalScrollSpeed != 0) { PImage tmpimg = currentPImage.copy(); tmpimg.resize(round(scalesize), round(scalesize)); PImage recur = createImage(tmpimg.width, tmpimg.height, ARGB); tmpimg.loadPixels(); recur.loadPixels(); if (horizontalScrollSpeed != 0 || priorHorizontalScrollSpeed != 0) { int speed = (horizontalScrollSpeed != 0) ? horizontalScrollSpeed : priorHorizontalScrollSpeed ; if (horizontalScrollSpeed == 0) { if (speed < 0 && horizontalScrollLocation >= 0 && (horizontalScrollLocation+speed) < 0) { priorHorizontalScrollSpeed = speed = - horizontalScrollLocation ; } else if (speed > 0 && (horizontalScrollLocation+speed) >= tmpimg.width) { priorHorizontalScrollSpeed = speed = tmpimg.width - horizontalScrollLocation ; } } horizontalScrollLocation = (horizontalScrollLocation + speed + tmpimg.width) % tmpimg.width ; int delta = (speed + tmpimg.width) % tmpimg.width ; for (int x = 0 ; x < tmpimg.width ; x++) { for (int y = 0 ; y < tmpimg.height ; y++) { int tox = (x + delta) % tmpimg.width ; recur.pixels[y*tmpimg.width+tox] = tmpimg.pixels[y*tmpimg.width+x] ; } } if (verticalScrollSpeed != 0 || verticalScrollLocation != 0) { // prep buffers for vertical as well recur.updatePixels(); tmpimg = recur ; recur = tmpimg.copy(); tmpimg.loadPixels(); recur.loadPixels(); } if (horizontalScrollSpeed == 0 && horizontalScrollLocation == 0) { priorHorizontalScrollSpeed = 0 ; } } if (verticalScrollSpeed != 0 || priorVerticalScrollSpeed != 0) { int speed = (verticalScrollSpeed != 0) ? verticalScrollSpeed : priorVerticalScrollSpeed ; if (verticalScrollSpeed == 0) { if (speed < 0 && verticalScrollLocation >= 0 && (verticalScrollLocation+speed) < 0) { priorVerticalScrollSpeed = speed = - verticalScrollLocation ; } else if (speed > 0 && (verticalScrollLocation+speed) >= tmpimg.width) { priorVerticalScrollSpeed = speed = tmpimg.width - verticalScrollLocation ; } } verticalScrollLocation = (verticalScrollLocation + speed + tmpimg.height) % tmpimg.height ; int delta = (speed + tmpimg.height) % tmpimg.height ; System.arraycopy(tmpimg.pixels, 0, recur.pixels, delta * tmpimg.width, (tmpimg.height - delta) * tmpimg.width); System.arraycopy(tmpimg.pixels, (tmpimg.height - delta) * tmpimg.width, recur.pixels, 0, delta * tmpimg.width); if (verticalScrollSpeed == 0 && verticalScrollLocation == 0) { priorVerticalScrollSpeed = 0 ; } } recur.updatePixels(); currentPImage = recur ; loadNextCell(false); advanceAfterLoadNextCell(); } } /* SKIP THIS WHOLE THING, NOT EFFECTIVE void processRotateShear() { if (roterAngleSpeed != 0) { float prex = roterAngle ; roterAngle += roterAngleSpeed ; if (roterAbating && (prex < 0 && roterAngle > 0 || prex > 0 && roterAngle < 0)) { roterAngle = roterAngleSpeed = 0 ; roterAbating = false ; if (currentTiler == nextTiler && currentTileVariant == nextTileVariant) { // force a repaint currentTiler = null ; } } if (roterAngle != 0) { translate(width/2, height/2); rotate(roterAngle); translate(-width/2, -height/2); // println("DEBUG SHEARING X = " + roterAngle); if (currentTiler == nextTiler && currentTileVariant == nextTileVariant) { // force a repaint currentTiler = null ; } if (roterAngle <= -TWO_PI || roterAngle >= TWO_PI) { roterAngle = abs(roterAngle) - TWO_PI ; } } } if (shearxAngleSpeed != 0) { float prex = shearxAngle ; shearxAngle += shearxAngleSpeed ; if (shearXabating && (prex < 0 && shearxAngle > 0 || prex > 0 && shearxAngle < 0)) { shearxAngle = shearxAngleSpeed = 0 ; shearXabating = false ; if (currentTiler == nextTiler && currentTileVariant == nextTileVariant) { // force a repaint currentTiler = null ; } } if (shearxAngle != 0) { // DO WITHIN CELL translate(width/2, height/2); // DO WITHIN CELL shearX(shearxAngle); // DO WITHIN CELL translate(-width/2, -height/2); // println("DEBUG SHEARING X = " + shearxAngle); if (currentTiler == nextTiler && currentTileVariant == nextTileVariant) { // force a repaint currentTiler = null ; } if (shearxAngle <= -HALF_PI/1.5 || shearxAngle >= HALF_PI/1.5) { if (shearxAngle <= -HALF_PI/1.5) { shearxAngleSpeed = abs(shearxAngleSpeed) ; } else { shearxAngleSpeed = -abs(shearxAngleSpeed) ; } } } } if (shearyAngleSpeed != 0) { float prey = shearyAngle ; shearyAngle += shearyAngleSpeed ; if (shearYabating && (prey < 0 && shearyAngle > 0 || prey > 0 && shearyAngle < 0)) { shearyAngle = shearyAngleSpeed = 0 ; shearYabating = false ; if (currentTiler == nextTiler && currentTileVariant == nextTileVariant) { // force a repaint currentTiler = null ; } } if (shearyAngle != 0) { // DO WITHIN CELL translate(width/2, height/2); // DO WITHIN CELL shearY(shearyAngle); // DO WITHIN CELL translate(-width/2, -height/2); if (currentTiler == nextTiler && currentTileVariant == nextTileVariant) { // force a repaint currentTiler = null ; } if (shearyAngle <= -HALF_PI/1.5 || shearyAngle >= HALF_PI/1.5) { if (shearyAngle <= -HALF_PI/1.5) { shearyAngleSpeed = abs(shearyAngleSpeed) ; } else { shearyAngleSpeed = -abs(shearyAngleSpeed) ; } } } } } */ void extractstableDisplayArray(List tileList) { maxcol = maxrow = 0 ; for (StatefulTile st : tileList) { if (st.col > maxcol) { maxcol = st.col ; } if (st.row > maxrow) { maxrow = st.row ; } } stableDisplayArray = new StatefulTile [maxcol+1][maxrow+1]; for (StatefulTile st : tileList) { stableDisplayArray[st.col][st.row] = st ; } if (scalesize != toscalesize) { isGrowingShrinking = true ; // Do this only after a stable Image/transform update } } String cmdbuffer = "" ; void keyPressed() { try { if (key == CODED || key == '~') { if (key == '~') { if (! sinTransit) { sinTransit = true ; sinAbating = false ; sinAngle = 0 ; intransit = true ; } else { sinAbating = true ; } println("sinTransit = " + sinTransit + ", sinAbating = " + sinAbating); printVariants(); return ; } if (keyCode == DOWN) { intransit = true ; if (verticalTransit < 1) { verticalTransit++ ; if (verticalTransit == 0) { verticalAbating = true ; } else { verticalAbating = false ; } } println("verticalTransit = " + verticalTransit + ", verticalAbating = " + verticalAbating); } else if (keyCode == UP) { intransit = true ; if (verticalTransit > -1) { verticalTransit-- ; if (verticalTransit == 0) { verticalAbating = true ; } else { verticalAbating = false ; } } println("verticalTransit = " + verticalTransit + ", verticalAbating = " + verticalAbating); } else if (keyCode == RIGHT) { intransit = true ; if (horizontalTransit < 1) { horizontalTransit++ ; if (horizontalTransit == 0) { horizontalAbating = true ; } else { horizontalAbating = false ; } } println("horizontalTransit = " + horizontalTransit + ", horizontalAbating = " + horizontalAbating); } else if (keyCode == LEFT) { intransit = true ; if (horizontalTransit > -1) { horizontalTransit-- ; if (horizontalTransit == 0) { horizontalAbating = true ; } else { horizontalAbating = false ; } } println("horizontalTransit = " + horizontalTransit + ", horizontalAbating = " + horizontalAbating); } printVariants(); return ; } if (key == 'F') { isFrozen = ! isFrozen ; cmdbuffer = "" ; } else if (key == 'b') { isBackground = false ; cmdbuffer = "" ; } else if (key == 'B') { isBackground = true ; cmdbuffer = "" ; } else if (key == 't') { isPrinting = ! isPrinting ; println("isPrinting = " + isPrinting); currentTiler = null ; // force redraw cmdbuffer = "" ; } else if (key == 'P') { isPlotting = ! isPlotting ; println("isPlotting = " + isPlotting); cmdbuffer = "" ; } else if (key == 'p' || key == 'c' || key == 'r' || key == 'h' || key == 'v' || key == 'H' || key == 'V' || key == 'i' || key == 'I' // h & v sine wave morph, 0 dead stop, <0 fade, >0 start || key == '^' || key == '<' || key == '>' // scrolling display /* || key == 'X' || key == 'Y' || key == 'R' */) { cmdbuffer = "" + key ; // println("DEBUG cmdbuffer " + cmdbuffer); } else if (key == 's') { cmdbuffer = "s" ; } else if (key == 'T') { cmdbuffer = "T" ; } else if (key == 'N') { cmdbuffer = "N" ; } else if (key == 'A') { // Advance 'a' with shredding - put new image behind old, gradually kill old. loadNextCell(true); nextTiler = tilers[nextTileIndex] ; nextDisplayList = nextTiler.tileDisplay(nextTileVariant, false); shredding = true ; shreadCountdown = 0 ; verticalAbating = horizontalAbating = transitAbating = false ; // Freeze display changes, just decay them where they are. intransit = indecay = sinTransit = sinAbating = false ; sinAngle = 0 ; verticalTransit = horizontalTransit = 0 ; isStableTiler = true ; } else if (key == 'a') { // ADVANCE IMAGE loadNextCell(true); advanceAfterLoadNextCell(); } else if (key == '\n') { println("DEBUG after newline, cmdbuffer = '" + cmdbuffer + "'"); if (cmdbuffer.length() > 1) { cmdbuffer = cmdbuffer.trim(); if (cmdbuffer.startsWith("p") || cmdbuffer.startsWith("c")) { interpretCommand(cmdbuffer, 0); } else if (cmdbuffer.startsWith("s")) { float floatval = 0 ; floatval = Float.parseFloat(cmdbuffer.substring(1)); floatval = constrain(floatval, 20, 4000); interpretCommand(null, floatval); /* DEPRECATED } else if (cmdbuffer.startsWith("X")) { float floatval = 0 ; floatval = Float.parseFloat(cmdbuffer.substring(1)); floatval = constrain(floatval, -TWO_PI, 10*TWO_PI); if (floatval > TWO_PI) { shearXabating = true ; // currentTiler = null ; } else { shearxAngleSpeed = floatval ; } } else if (cmdbuffer.startsWith("Y")) { float floatval = 0 ; floatval = Float.parseFloat(cmdbuffer.substring(1)); floatval = constrain(floatval, -TWO_PI, 10*TWO_PI); if (floatval > TWO_PI) { shearYabating = true ; // currentTiler = null ; } else { shearyAngleSpeed = floatval ; } } else if (cmdbuffer.startsWith("R")) { float floatval = 0 ; floatval = radians(Float.parseFloat(cmdbuffer.substring(1))); floatval = constrain(floatval, -TWO_PI, 1000); if (floatval > TWO_PI) { roterAbating = true ; // currentTiler = null ; } else { roterAngleSpeed = floatval ; } */ } else if (cmdbuffer.startsWith("T")) { int intval = 0 ; intval = Integer.parseInt(cmdbuffer.substring(1)); intval = constrain(intval, 0, tints.length-1); if (tintsIndex != intval) { tintsIndex = intval ; tintsOffset = 0 ; if (isStableTiler && ! intransit) { currentTiler = nextTiler = null ; // sledgehammer redraw } } } else if (cmdbuffer.startsWith("N")) { int intval = 0 ; intval = Integer.parseInt(cmdbuffer.substring(1)); intval = constrain(intval, 0, 8); if (nestLevel != intval) { nestLevel = intval ; println("DEBUG setting nestLevel to " + nestLevel); if (isStableTiler && ! intransit) { currentTiler = nextTiler = null ; // sledgehammer redraw } } } else if (cmdbuffer.startsWith("r")) { int intval = 0 ; intval = Integer.parseInt(cmdbuffer.substring(1)); intval = constrain(intval, 0, 10); if (toRecursion != intval) { toRecursion = intval ; if (atRecursion > toRecursion) { atRecursion = 0 ; // start over } println("DEBUG setting toRecursion to " + toRecursion + ", atRecursion at " + atRecursion); } } else if (cmdbuffer.startsWith("h") || cmdbuffer.startsWith("H")) { int intval = 0 ; intval = Integer.parseInt(cmdbuffer.substring(1)); intval = constrain(intval, -100, 100); toRecursion = atRecursion = 0 ; boolean inverted = cmdbuffer.startsWith("H") ; if (hSlideRate != intval || inverted != hSlideInvert) { hSlideRate = intval ; hSlideProgress = -1 ; hSlideInvert = inverted ; println("DEBUG setting hSlideRate to " + hSlideRate + ", hSlideProgress at " + hSlideProgress + ", hSlideInvert = " + hSlideInvert); } horizontalSineGo = false; horizontalSineInvert = false ; horizontalSineToGo = 0 ; verticalSineGo = false; verticalSineInvert = false ; verticalSineToGo = 0 ; } else if (cmdbuffer.startsWith("v") || cmdbuffer.startsWith("V")) { int intval = 0 ; intval = Integer.parseInt(cmdbuffer.substring(1)); intval = constrain(intval, -100, 100); toRecursion = atRecursion = 0 ; boolean inverted = cmdbuffer.startsWith("V") ; if (vSlideRate != intval || inverted != vSlideInvert) { vSlideRate = intval ; vSlideProgress = -1 ; vSlideInvert = inverted ; println("DEBUG setting vSlideRate to " + vSlideRate + ", vSlideProgress at " + vSlideProgress + ", vSlideInvert = " + vSlideInvert); } horizontalSineGo = false; horizontalSineInvert = false ; horizontalSineToGo = 0 ; verticalSineGo = false ; verticalSineInvert = false ; verticalSineToGo = 0 ; } else if (cmdbuffer.startsWith("i") || cmdbuffer.startsWith("I")) { float fval = Float.parseFloat(cmdbuffer.substring(1)); int ival = constrain(int(fval),-10,10); float fraction = abs(fval - int(fval)); toRecursion = atRecursion = 0; hSlideRate = hSlideProgress = 0 ; vSlideRate = vSlideProgress = 0 ; hSlideInvert = vSlideInvert = false ; hSineDeviation = vSineDeviation = 0 ; boolean isvertical = cmdbuffer.startsWith("I") ; if (isvertical) { if (vSineWaveProgress != null) { Arrays.fill(vSineWaveProgress, 0); } if (ival == 0) { verticalSineGo = false ; // stop it dead verticalSineInvert = false ; verticalSineToGo = 0 ; } else if (ival < 0) { verticalSineGo = true ; verticalSineInvert = true ; // go up verticalSineToGo = - ival ; if (vSineDeviation != fraction) { vSineDeviation = fraction ; hSineWaveSteps = vSineWaveSteps = null ; } } else { verticalSineGo = true ; // start over, even if in progress verticalSineInvert = false ; verticalSineToGo = ival ; if (vSineDeviation != fraction) { vSineDeviation = fraction ; hSineWaveSteps = vSineWaveSteps = null ; } } } else { if (hSineWaveProgress != null) { Arrays.fill(hSineWaveProgress, 0); } if (ival == 0) { horizontalSineGo = false ; // stop it dead horizontalSineInvert = false ; horizontalSineToGo = 0 ; } else if (ival < 0) { horizontalSineGo = true ; // start over, even if in progress horizontalSineInvert = true ; // go left horizontalSineToGo = - ival ; if (hSineDeviation != fraction) { hSineDeviation = fraction ; hSineWaveSteps = vSineWaveSteps = null ; } } else { horizontalSineGo = true ; // start over, even if in progress horizontalSineInvert = false ; horizontalSineToGo = ival ; if (hSineDeviation != fraction) { hSineDeviation = fraction ; hSineWaveSteps = vSineWaveSteps = null ; } } } } else if (cmdbuffer.startsWith("^") || cmdbuffer.startsWith("<") || cmdbuffer.startsWith(">")) { // scrolling display int ival = 0 ; ival = constrain(Integer.parseInt(cmdbuffer.substring(1)),-10,10); if (cmdbuffer.startsWith("^")) { priorVerticalScrollSpeed = (ival == 0) ? verticalScrollSpeed : 0 ; verticalScrollSpeed = ival ; } else { priorHorizontalScrollSpeed = (ival == 0) ? horizontalScrollSpeed : 0 ; horizontalScrollSpeed = ival ; } } } cmdbuffer = "" ; } else if (cmdbuffer.length() > 0) { cmdbuffer = cmdbuffer + key ; } } catch (Exception nfx) { println("Exception on cmd: '" + cmdbuffer + "', key = '" + key + "': " + nfx.getMessage()); cmdbuffer = "" ; } printVariants(); } void advanceAfterLoadNextCell() { // Called after 'a' and also 'RN' for recursive image if (! isStableTiler) { // println("DEBUG 'a' case 0"); nextTiler = tilers[nextTileIndex] ; currentDisplayList = nextDisplayList = nextTiler.tileDisplay(nextTileVariant, false); for (StatefulTile st : nextDisplayList) { if (stableDisplayArray.length > st.col && stableDisplayArray[st.col].length > st.row) { StatefulTile oldt = stableDisplayArray[st.col][st.row]; st.tox = oldt.tox ; st.toy = oldt.toy ; st.torot = oldt.torot ; st.toscalex = oldt.toscalex ; st.toscaley = oldt.toscaley ; st.x = oldt.x ; st.y = oldt.y ; st.rot = oldt.rot ; st.scalex = oldt.scalex ; st.scaley = oldt.scaley ; } } // DEBUG extractstableDisplayArray(nextDisplayList); } else if (! intransit) { // println("DEBUG 'a' case 1"); currentTiler = nextTiler = null ; // sledgehammer redraw at a new scale } else { // println("DEBUG 'a' case 2"); currentTiler = nextTiler = tilers[nextTileIndex] ; currentTileVariant = nextTileVariant ; currentDisplayList = nextDisplayList = currentTiler.tileDisplay(nextTileVariant, false); } } void interpretCommand(String cmd, float scaler) { if (cmd == null) { // This is a scalesize change. if (scaler != scalesize || scaler != toscalesize) { toscalesize = scaler ; // DEBUG DALE defer currentTiler = nextTiler = null ; // sledgehammer redraw at a new scale // DEBUG defer loadNextCell(false); // Uses new scalesize for Cell. isGrowingShrinking = (isStableTiler && currentTiler == nextTiler && currentTileVariant == nextTileVariant && (!intransit) && (!indecay)); // System.err.println("DEBUG interpretCommand toscalesize = " + toscalesize // + ", scalesize = " + scalesize + ", isGrowingShrinking = " + isGrowingShrinking); } } else { int variant = 0 ; int uline = cmd.indexOf('.'); // e.g., pm_1, cm_1 if (uline > -1) { try { // println("DEBUG BEFORE CHOPPING, cmd = '" + cmd + "'"); variant = Integer.parseInt(cmd.substring(uline+1)); cmd = cmd.substring(0,uline); println("DEBUG parsed cmd = '" + cmd + "', variant = " + variant); } catch (NumberFormatException xx) { println("Invalid variant number, using 0: " + cmd); variant = 0 ; cmd = cmd.substring(0,uline); } } if (cmd.equals(nextTileCommand) && variant != nextTileVariant) { if (variant < 0) { variant = 0 ; } else if (variant >= nextTiler.getNumberOfVariants()) { println(cmd + " variants only go up to " + (nextTiler.getNumberOfVariants()-1)); variant = nextTiler.getNumberOfVariants()-1; } } if ((! cmd.equals(nextTileCommand)) || variant != nextTileVariant) { if (! nextTileCommand2Index.containsKey(cmd)) { println("Invalid command: " + cmd); } else { nextTileIndex = nextTileCommand2Index.get(cmd); nextTileCommand = cmd ; nextTiler = tilers[nextTileIndex]; if (variant < 0) { variant = 0 ; } else if (variant >= nextTiler.getNumberOfVariants()) { variant = nextTiler.getNumberOfVariants()-1; println(cmd + " variants only go up to " + (nextTiler.getNumberOfVariants()-1)); } nextTileVariant = variant ; nextDisplayList = nextTiler.tileDisplay(nextTileVariant, false); isStableTiler = false ; for (StatefulTile st : nextDisplayList) { if (stableDisplayArray.length > st.col && stableDisplayArray[st.col].length > st.row) { StatefulTile oldt = stableDisplayArray[st.col][st.row]; oldt.tox = st.tox ; oldt.toy = st.toy ; oldt.torot = st.torot ; oldt.toscalex = st.toscalex ; oldt.toscaley = st.toscaley ; } } /* for (StatefulTile st : nextDisplayList) { if (stableDisplayArray.length > st.col && stableDisplayArray[st.col].length > st.row) { StatefulTile oldt = stableDisplayArray[st.col][st.row]; oldt.tox = st.tox = oldt.tox ; oldt.toy = st.toy = oldt.toy ; oldt.torot = st.torot = oldt.torot ; oldt.toscalex = st.toscalex = oldt.toscalex ; oldt.toscaley = st.toscaley = oldt.toscaley ; } } */ } } } } // printname handles text on screen and Movie Maker dumps void printname(String txt) { txt = txt.replaceAll(" ",""); // println(txt); txt = imgFileNames.get(imgloader.getDisplayedImageIndex()) + "_" + txt ; if (isPrinting) { fill(0); stroke(0); textMode(SHAPE); textAlign(LEFT); textSize(48); text(txt, 24, height - 50); } if (isPlotting) { String tmptxt = txt.replaceAll(" ",""); saveFrame("movies/#####_" +tmptxt+ "_.png"); // isPlotting = false ; // DEBUG } }