// SunflowersFall2020.pde Parson simple demo of tracking a // graphical sun across a 2D canvas, D. Parson, 3/27/2020, // updated 10/22/2020 to demo blendMode() & filter() for CSC220. // field is 626 X 417 pixels, making window 1252 X 834 for now, // although it is OK to stretch the background field as needed. PImage field, sunflower, sun ; // store the image files in memory here // Parson's Sunflower.png pads the bottom of the image with transparent pixels // equal to the image's height in order to put rotation center at image's center Avatar SunObject ; float ambientBrightness = 1.0 ; // scale down during night in the sun's move() int sunSpeed = 1 ; // speed across the screen in pixels per draw int flowerCount = 15 ; Avatar [] FlowerObjects = new Avatar [flowerCount] ; // empty array at start void setup() { // This function runs once at startup time. size(1252, 834, P2D); randomSeed(12345); // for testing, repeat "random" locations of sunflowers field = loadImage("field.jpg"); // images are in sketch directory sunflower = loadImage("Sunflower.png"); // PNG required for transparent margins sun = loadImage("sunWglasses.png"); float sunscale = .25 ; // I found this number using trial & error SunObject = new Avatar(width/2, sun.height*sunscale, sunscale, 0, sun); float flowerscale = .125 ; // also found via trial & error for (int i = 0 ; i < FlowerObjects.length ; i++) { FlowerObjects[i] = new Avatar( random(sunflower.width*flowerscale, width-sunflower.width*flowerscale), height-.5*random(height/3,height/2), flowerscale, 0, sunflower); } } void draw() { // This function runs at frame rate, default is every 60th of a second. imageMode(CORNER); blendMode(BLEND); // default blend mode, restore normal plotting // anchor upper left of field at upper left of display, 0,0, fit onto display tint(ambientBrightness*255); // sun's move() makes this darker at night image(field, 0, 0, width, height); // BLENDS GO FOR THE SUNFLOWERS, USE DEFAULT blendMode(BLEND) elsewhere. // https://processing.org/reference/blendMode_.html // blendMode(BLEND); // blendMode(ADD); // blendMode(SUBTRACT); // !!! // blendMode(DARKEST); // blendMode(LIGHTEST); // entire PNG, and tint has no effect on that. // blendMode(DIFFERENCE); // not supported on my Mac // blendMode(EXCLUSION); // entire PNG, XOR towards mud, use lightly // blendMode(MULTIPLY); // entire PNG // blendMode(SCREEN); // entire PNG // blendMode(REPLACE); for (Avatar flower : FlowerObjects) { flower.move(); flower.display(); } blendMode(BLEND) ; // default blend mode, restore normal plotting // filter(BLUR,10); // filter affects everything plotted before this. SunObject.display(); SunObject.move(); // FILTERS GO AFTER THE DISPLAYS THAT YOU WANT TO AFFECT. // https://processing.org/reference/filter_.html // filter(GRAY); // Converts any colors in the image to grayscale equivalents. // No parameter is used. // filter(OPAQUE); // Sets the alpha channel to entirely opaque. No parameter is used. // filter(INVERT); // Sets each pixel to its inverse value. No parameter is used. // filter(POSTERIZE,2); // Limits each channel of the image to the number of colors specified as // the parameter. The parameter can be set to values between 2 and 255, // but results are most noticeable in the lower ranges. // filter(BLUR,10); // Executes a Guassian blur with the level parameter specifying the extent // of the blurring. If no parameter is used, the blur is equivalent to // Gaussian blur of radius 1. Larger values increase the blur. // filter(ERODE); // Reduces the light areas. No parameter is used. // filter(DILATE); // Increases the light areas. No parameter is used. } class Avatar { // A class is the easiest way to keep track of location, scale, rotation. float x, y ; // location of bottom of stem, make floats for move()'s trig float scaling ; // fit onto the screen, see code comments below float rotation ; // used to track the location of the sun PImage img ; // sunflower, sun, or other to plot Avatar(float bottomx, float bottomy, float myscale, float myrotation, PImage myimg) { // store initial values within constructor function: x = bottomx ; y = bottomy ; scaling = myscale ; rotation = myrotation ; img = myimg ; } void display() { // show this Avatar. If it is not the sun, rotate it towards the sun. push(); // save global coordinates & appearance for restore via pop() imageMode(CENTER); translate(x, y); // move the PNG image to its position scale(scaling); rotate(rotation); image(img, 0, 0); pop(); } void move() { if (this == SunObject) { // just moves left-to-right for now. An arc would be nice. x = x + sunSpeed ; if (x >= width*1.5) { // wrap back around from right to left of display, after going through "night" // Sun goes 1/2 a screen width to right, then comes back from 1/2 screen width // to the left, during the night. x = -width/2.0 ; } ambientBrightness = 1.0 ; if (x < 0) { // the farther to the left, the darker, constrain to no less than 25% brightness ambientBrightness = constrain((x - (-width/2.0)) / (width/2.0),.25, 1.0); } else if (x > width) { // the farther to the right, the darker ambientBrightness = constrain(1.0 - ((x - width) / (width/2.0)),.25, 1.0); } } else { // This is a sunflower, here is the trig for free. float virtualx = SunObject.x ; if (SunObject.x > width) { virtualx = width - (SunObject.x - width); // virtualx goes back towards vertical after sunset } else if (SunObject.x < 0) { // virtualx goes from vertical to left side as sun returns to the daytime virtualx = 0 - SunObject.x ; } if (virtualx < x) { // sun is upper left rotation = (-PI/2)+atan((SunObject.y - y) / (virtualx - x)); } else if (virtualx > x) { // sun is upper right rotation = (PI/2)+atan((SunObject.y - y) / (virtualx - x)); //println("DEBUG sunx " + SunObject.x + " rotation " + degrees(rotation)); } else { // If sun is directly overhead, do not rotate. rotation = 0 ; } } } }