// CSC480DemoScreenshot1, D. Parson, warmup to CSC480 assn3, spring 2020 //<>// // I need to dicument the key commands & the code int globalRadius = 0 ; // setup() to min(width/height)/2 PImage clippingCircle = null ; // build this in setup() after we have size PImage lastCanvas = null ; char shapeToDraw = 'a' ; // valid shapes are 'a' through 'c' final char topShape = 'd' ; float rotateCanvasAmount = 0.0, rotateCanvasSpeed = 0.0 ; float scaleCanvas = 1.0 ; final float rotateCanvasIncr = radians(0.1); final float scaleCanvasIncr = 0.1 ; int mousePressedX = -1, mousePressedY = -1, mouseReleasedX = -1, mouseReleasedY = -1 ; int hue ; Brush [] brushes = new Brush [ 0 ] ; boolean reflectx = false, reflecty = false ; PImage canvasBrushTrimmed = null ; void setup() { size(1500, 1000, P2D); globalRadius = min(width,height)/2; // Build a clipping circle based on screen size. int midx = width / 2 ; int midy = height / 2 ; colorMode(RGB, 256, 256, 256, 256); // Just to build clippingCircle clippingCircle = createImage(width, height, ARGB); clippingCircle.loadPixels(); for (int col = 0 ; col < width ; col++) { for (int row = 0 ; row < height ; row++) { int pix = row * width + col ; if (dist(col, row, midx, midy) > globalRadius) { clippingCircle.pixels[pix] = 0x0ff000000 ; // all black, max alpha } else { clippingCircle.pixels[pix] = 0x000000000 ; // min alpha is transparent } } } clippingCircle.updatePixels(); colorMode(HSB, 360, 100, 100, 100); rectMode(CENTER); ellipseMode(CENTER); shapeMode(CENTER); imageMode(CENTER); background(0); Thread trimthread = new Thread(trimmer); trimthread.start(); } void keyPoll() { if (keyPressed) { if (key == CODED) { if (keyCode == UP) { scaleCanvas += scaleCanvasIncr ; } else if (keyCode == DOWN) { scaleCanvas -= scaleCanvasIncr ; } else if (keyCode == LEFT) { rotateCanvasSpeed -= rotateCanvasIncr ; } else if (keyCode == RIGHT) { rotateCanvasSpeed += rotateCanvasIncr ; } } else if (key == '<') { hue = (hue + 360 - 4) % 360 ; // same as -1 in modulo 360 circle } else if (key == '>') { hue = (hue + 4) % 360 ; } } } void keyPressed() { if (key >= 'a' && key <= topShape) { shapeToDraw = key ; } else if (key == 'C') { // clear background(0); canvasBrushTrimmed = null ; lastCanvas = null ; } else if (key == 'R') { rotateCanvasAmount = rotateCanvasSpeed = 0.0 ; scaleCanvas = 1.0 ; } else if (key == 'L') { // lift the brushes brushes = new Brush [0]; } else if (key == '_') { reflecty = ! reflecty ; println("reflecty = " + reflecty); } else if (key == '|') { reflectx = ! reflectx ; println("reflectx = " + reflectx); } else if (key == '/') { reflectx = reflecty = false ; println("no reflection"); } } void mousePressed() { mousePressedX = mouseX - width/2 ; // 0,0 is at center of display mousePressedY = mouseY - height/2 ; } void mouseReleased() { mouseReleasedX = mouseX - width/2 ; mouseReleasedY = mouseY - height/2 ; brushes = (Brush []) append(brushes, new Brush(shapeToDraw, mousePressedX, mousePressedY, mouseReleasedX, mouseReleasedY, hue)); } void draw() { PImage oldbrush = trimmedCanvasBrushQueue.poll(); if (oldbrush != null) { canvasBrushTrimmed = oldbrush ; } push(); translate(width/2, height/2); // 0,0 is at center of display keyPoll(); if (lastCanvas != null) { push(); scale(scaleCanvas); rotate(rotateCanvasAmount); image(lastCanvas, 0, 0, lastCanvas.width, lastCanvas.height); pop(); } if (rotateCanvasSpeed != 0) { rotateCanvasAmount += rotateCanvasSpeed ; } for (Brush brush : brushes) { brush.display(); if (reflectx) { pushMatrix(); scale(-1, 1); brush.display(); popMatrix(); } if (reflecty) { pushMatrix(); scale(1, -1); brush.display(); popMatrix(); } if (reflectx && reflecty) { pushMatrix(); scale(-1, -1); brush.display(); popMatrix(); } } image(clippingCircle, 0, 0, width, height); lastCanvas = photographCanvas(null); untrimmedCanvasBrushQueue.offer(lastCanvas); pop(); // After all snapshot and clipping is done, paint the color pallette in the upper left. Brush pallette = new Brush('b', 0, 0, 100, 100, hue); pallette.display(); } class Brush { char brushtype ; int startx, starty, endx, endy ; int Hue ; Brush (char brushtype, int startx, int starty, int endx, int endy, int Hue) { this.brushtype = brushtype ; this.startx = startx ; this.starty = starty ; this.endx = endx ; this.endy = endy ; this.Hue = Hue ; } void display() { push() ; rectMode(CENTER); ellipseMode(CENTER); shapeMode(CENTER); imageMode(CENTER); colorMode(HSB, 360, 100, 100, 100); translate((startx+endx)/2, (starty+endy)/2); noStroke(); fill(Hue, 99, 99, 99); int w = abs(startx-endx) ; int h = abs(starty-endy) ; switch (brushtype) { case 'a': ellipse(0, 0, w, h); break ; case 'b': rect(0, 0, w, h); break ; case 'c': case 'd': default: if (canvasBrushTrimmed != null) { if (brushtype == 'd') { tint(hue,99,99); } image(canvasBrushTrimmed, 0, 0, w, h); } break ; } pop(); } } PImage photographCanvas(PImage canvas) { int [] pix ; PImage result ; if (canvas != null) { canvas.loadPixels(); pix = canvas.pixels ; result = createImage(canvas.width, canvas.height, ARGB); } else { loadPixels(); // from display buffer pix = pixels ; result = createImage(width, height, ARGB); } result.loadPixels(); java.lang.System.arraycopy(pix, 0, result.pixels, 0, pix.length); result.updatePixels(); if (canvas != null) { canvas.updatePixels(); } else { updatePixels(); // main display, always match a loadPixels() call with updatePixels(); } return result ; } import java.util.concurrent.SynchronousQueue ; final SynchronousQueue untrimmedCanvasBrushQueue = new SynchronousQueue(); final SynchronousQueue trimmedCanvasBrushQueue = new SynchronousQueue(); final Trimmer trimmer = new Trimmer(); class Trimmer implements java.lang.Runnable { void run() { while (true) { // This server runs forever to trim PImages doesn to their central circle. PImage incoming, outgoing ; try { incoming = untrimmedCanvasBrushQueue.take(); // blocks til new screenshot arrives int diameter = min(incoming.width, incoming.height); int radius = diameter / 2 ; int xmargin = (incoming.width - diameter) /2 ; int ymargin = (incoming.height - diameter) / 2 ; outgoing = createImage(diameter, diameter, ARGB); incoming.loadPixels(); outgoing.loadPixels(); int incenterx = incoming.width/2 ; int incentery = incoming.height/2 ; java.util.Arrays.fill(outgoing.pixels, 0); // all transparent except central circle for (int inrow = ymargin, outrow = 0 ; inrow < incoming.height - ymargin ; inrow++, outrow++) { for (int incol = xmargin, outcol = 0 ; incol < incoming.width - xmargin ; incol++, outcol++) { if (dist(incol, inrow, incenterx, incentery) < radius) { outgoing.pixels[outrow * outgoing.width + outcol] = incoming.pixels[inrow * incoming.width + incol]; } } } trimmedCanvasBrushQueue.put(outgoing); } catch (InterruptedException iex) { println("WARNING, unexpected interrupted exception: " + iex.getMessage()); } } } }