/* A program that uses masking, reflection, transformation and basic physics to create a simple kaleidoscope effect. Mouse clicks change the x and y values of the ball within the center box and reflect that change in the larger background pattern. @author: Marie Abigail Meriel @date: October 3, 2015 */ //so many global variablessssssssss boolean fillShape = false; //random value that indicates if a shape is filled or not color strokeColor; //collection of rgb values int r, g, b; //random values used to choose color int x1, x2, x3, x4; //vertices used to build random shapes int y1, y2, y3, y4; PGraphics triangle, pattern; //triangle = mask, pattern = pattern to be cut PImage mirrorHorizontal, mirrorVertical; //used to mirror canvas PImage patternImage, patternImageBase; //images of the pattern made float x = 400; //origin x and y where background pattern is positioned float y = 488; float speed = 5; //speed at which ball moves float gravity = 1; //increases speed as it goes down; mocks gravity boolean applyPhysics = false; // boolean to determine whether the ball has been moved int bounceTracker = 0; //used to track bounces to stop endless bouncing int radian = 0; //used to determine the degree at which the pattern is rotated boolean messageDismissed = false; //used to determine if message has been seen or not void setup() { size(800, 800); //create the mask triangle = createGraphics(width, height, JAVA2D); triangle.beginDraw(); triangle.background(0); triangle.fill(255); triangle.triangle(400, 400, 800, 400, 800, 0); triangle.endDraw(); //create patternImage and snapshot images createPattern(); patternImageBase = pattern.get(); //picture of the original image patternImage = pattern.get(); //cut image patternImage.mask(triangle.get()); } void draw() { pattern.pushMatrix(); pattern.imageMode(CENTER); pattern.beginDraw(); //position the image relative to the ball in the middle pattern.translate((x - 250) * 4, (y - 300) * 4); //radians are defined by key presses pattern.rotate(radians(radian)); pattern.image(patternImageBase, 0, 0); pattern.endDraw(); pattern.popMatrix(); patternImage = pattern.get(); // get the pattern image patternImage.mask(triangle.get()); //cut the pattern according to the mask image image(patternImage, 0, 0); //show the pattern image(patternImage.get(), 0, 0); //reflect the cut pattern pushMatrix(); translate(800, 800); scale(-1, 1); rotate(radians(-90)); image(patternImage.get(), 0, 0); popMatrix(); //mirror the canvas horizontally mirrorHorizontal = get(400, 0, 400, 400); translate(400, 0); scale(-1, 1); image(mirrorHorizontal, 0, 0); //mirror the canvas vertically mirrorVertical = get(0, 0, 800, 400); translate(-400, 800); scale(1, -1); image(mirrorVertical, 0, 0); //draw the miniature pattern to show where x/y is drawRectangle(); //draw ball //translating because it appears on the top part of the box pushMatrix(); translate(400, 400); scale(-1, -1); translate(-400, -400); drawEllipse(); popMatrix(); //if ball is in the air if (applyPhysics) { pattern.clear(); //clear the canvas to stop repetitive drawings y += speed; speed += gravity; if (y > 488) { speed *= -0.5; //if it hits the bottom of the box, change direction y += speed; bounceTracker++; //add to the bounce tracker if (bounceTracker > 5) { //stop endless bouncing speed = 0; gravity = 0; bounceTracker = 0; applyPhysics = false; resetValues(); } } else if (y < 312) { //if it hits the top of the box, change direction speed *= 0.5; y += speed; } } //if the message hasn't been dismissed, display the message if (!messageDismissed){ messageDisplay(); } } //color generator void setStrokeColor() { r = int(random(256)); g = int(random(256)); b = int(random(256)); strokeColor = color(r, g, b); } //dictates if a shape is to be filled or not void setFillShape() { int i = int(random(2)); if (i == 1) { fillShape = true; } else { fillShape = false; } } void drawTriangles() { //draw between 15 - 20 shapes int x = int(random(15, 20)); for (int i = 0; i < x; i++) { setStrokeColor(); //create a new color pattern.stroke(strokeColor); //set the new color to be the stroke color pattern.strokeWeight(5); //if the value of fillShape is true, set it to the same //rgb values set in setStrokeColor setFillShape(); if (fillShape) { pattern.fill(strokeColor); } else { pattern.noFill(); } //y is used to determine if the triangle is going to be upside down or not int y = int(random(2)); //let the starting point be anywhere within the canvas x1 = int(random(800)); y1 = int(random(800)); x2 = x1 - int(random(90, 150)); //triangles are no more than 300 in width x3 = x1 + int(random(90, 150)); //if y == 1, set the triangle right side up //else, turn it upside down if (y == 1) { y2 = y1 + int(random(90, 150)); y3 = y1 + int(random(90, 150)); } else { y2 = y1 - int(random(90, 150)); y3 = y1 - int(random(90, 150)); } //draw triangle with random vertices pattern.triangle(x1, y1, x2, y2, x3, y3); } } //draw random circles void drawCircles() { //draw between 15 - 20 circles int x = int(random(15, 20)); for (int i = 0; i < x; i++) { setStrokeColor(); //create a new color pattern.stroke(strokeColor); //set the color as the stroke color pattern.strokeWeight(5); //determine if the shape is to be filled; if it is, use the same color //as created in stroke color setFillShape(); if (fillShape) { pattern.fill(strokeColor); } else { pattern.noFill(); } //let the circle center be anywhere within the canvas x1 = int(random(800)); y1 = int(random(800)); x2 = int(random(50, 150));//let variable x2 define height and width pattern.ellipse(x1, y1, x2, x2); } } //draw random diamonds void drawDiamonds() { //draw between 15 and 20 diamonds int x = int(random(15, 20)); for (int i = 0; i < x; i++) { setStrokeColor(); //create a new color pattern.stroke(strokeColor); //set the color to be stroke color pattern.strokeWeight(5); //Determine if shape is to be filled or not; if it is, use the same color //created in setStrokeColor() setFillShape(); if (fillShape) { pattern.fill(strokeColor); } else { pattern.noFill(); } //let the diamond starting point be anywhere within the canvas x1 = int(random(800)); y1 = int(random(800)); x2 = int(random(50, 150));//variable x2 is used to define height and width //in order to rotate, the shape must first be translated //which takes in the center of the shape //so the first point is added to half the height and width to find its center x3 = x1 + (x2/2); //rotate shape pattern.pushMatrix(); pattern.translate(x3, x3); pattern.rotate(radians(45)); pattern.translate(x3 * -1, x3 * -1); pattern.rect(x1, y1, x2, x2); pattern.popMatrix(); } } //create the background pattern void createPattern() { pattern = createGraphics(width, height, JAVA2D); pattern.beginDraw(); pattern.background(0); //Create the pattern drawTriangles(); drawCircles(); drawDiamonds(); pattern.endDraw(); } //draw transparent black square with transparent pattern image //the background pattern rotates, but not the black square void drawRectangle() { noStroke(); fill(0, 200); pushMatrix(); rect(300, 300, 200, 200); translate(400, 400); rotate(radians(radian)); translate(-400, -400); tint(255, 100); //half the opacity of the image image(patternImageBase, 300, 300, 200, 200); tint(255, 255); //switch the opacity back to full popMatrix(); } //draw ball void drawEllipse() { fill(255); ellipse(x, y, 25, 25); } //if true, the ball's position has been moved void mousePressed() { //if the message has been dismissed, execute if (messageDismissed) { //if the mouse has been clicked within the center box if (((mouseX > 312) && (mouseX < 488)) && ((mouseY > 312) && (mouseY < 488))) { //apply physics to the ball and change the ball's x/y co-ordinates applyPhysics = true; x = mouseX; y = mouseY; } } else { //if the message hasn't been dismissed if (((mouseX > 200) && (mouseX < 600)) && ((mouseY > 200) && (mouseY < 600))){ messageDismissed = true; } } } //on event rotate the pattern image void keyPressed() { pattern.clear(); if (key == CODED) { if (keyCode == RIGHT) { radian += 45; } else if (keyCode == LEFT) { radian -= 45; } } } //reset speed and gravity values back to their original settings void resetValues() { speed = 5; gravity = 1; } //show the initial message that describes the kaleidoscope functionality void messageDisplay() { fill(0); rect(100, 100, 600, 600, 50); pushMatrix(); translate(650, 565); //center on which to flip scale(-1, -1); //flip it horizontally and vertically textSize(24); textAlign(CENTER); //make it look pretty fill(255); //set String objects to hold description //\n doesn't put the text in a new line :'( String description1 = "This is a kaleidoscope!"; String description2 = "In the middle of the kaleidoscope is a black square " + "and the background pattern. "; String description3 = "Change the pattern in the kaleidoscope by changing the " + "position of the ball."; String description4 = "Use the left and right arrow keys to rotate the pattern."; String description5 = "Click anywhere in this box to dismiss this message."; // text(description1, 0, 0, 500, 30); text(description2, 0, 40, 500, 90); text(description3, 0, 120, 500, 90); text(description4, 0, 200, 500, 90); fill(255, 0, 0); text(description5, 0, 275, 500, 90); popMatrix(); }