Processing - Zoom on clicked area then click again to zoom back out - java

I have a picture then used a flashlight type of light to only show where the mouse is hovering over. That part of the code works, but now I want to use if/else statements to zoom in on the selected area and then click again to zoom back out. Any other way to zoom in on specific area then back out of that area also helps. Really any help will be appreciated!
PImage ispy;
void setup () {
size(1024,768);
ispy = loadImage("ispy2.jpeg");
}
void draw () {
loadPixels();
ispy.loadPixels();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x+y*width;
float r = red(ispy.pixels[loc]);
float g = green(ispy.pixels[loc]);
float b = blue(ispy.pixels[loc]);
float d = dist(mouseX, mouseY, x, y); //
float factor = map(d, 0, 200, 2, 0);
pixels[loc] = color(r*factor, g*factor, b*factor);
}
}
updatePixels();
}

Here is my interpretation of what you are talking about. We store a isClicked boolean to store the state of whether we should zoom in or not. When we are going to draw the image, we translate() to the mouse, then we scale(), then we translate() back the same amount that we moved before, but in the opposite direction. What this does is it does the scale transform around the mouse position.
One thing that I couldn't find a way around way your way of updating the pixels directly from the image and the flashlight effect. What the program is doing instead is using your method to make a mask image and applying that to a PGraphics object. Another thing that I noticed is that when just rendering straight to the screen, there is considerable lag from the scaling. Instead, I have moved the drawing to a PGraphics object. This improves the performance.
In the end, to render, the program is drawing everything on the PGraphics object, then applying the mask to that object to get the flashlight effect.
Here is the code that I have:
PImage ispy, distMask;
boolean isClicked = false;
PGraphics renderer;
void createDistanceMask(PImage distMask){ //Takes image and changes its pixels to "alpha" for the PGraphics renderer
distMask.loadPixels();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x+(height-y-1)*width;
float d = dist(mouseX, mouseY, x, y); //
int factor = int(map(d, 0, 200, 400, 0)); //Pixel data will be between 0 and 255, must scale down later.
if (factor > 255)
factor = 255;
distMask.pixels[loc] = color(factor,factor,factor);
}
}
distMask.updatePixels();
}
void setup () {
size(1024,768, P2D);
ispy = loadImage("ispy2.jpeg");
distMask = new PImage(width,height);
renderer = createGraphics(width,height,P2D);
mouseX = width/2; //Not necessary, but will have black screen until mouse is moved
mouseY = height/2;
}
void draw () {
background(0);
pushMatrix();
createDistanceMask(distMask);
renderer.beginDraw(); //Starts processing stuff into PGraphics object
renderer.background(0);
if(isClicked){ //This is to get the zoom effect
renderer.translate(mouseX, mouseY);
renderer.scale(2);
renderer.translate(-mouseX, -mouseY);
}
renderer.image(ispy,0,0); //Render Image
renderer.endDraw();
renderer.mask(distMask); //Apply Distance mask for flashlight effect
image(renderer,0,0); //Draw renderer result to screen
popMatrix();
}
void mouseClicked(){
isClicked = !isClicked;
}
In my comment, I asked about having the screen move to the mouse, which is what this is doing. If you want to "freeze" the screen in one position, what you can do is store a lastMouseClickPosition PVector or simply just ints. Then, when translating, translate to the position instead of the PVector.
Here's the code that would change:
PVector lastClickPos = new PVector(); //Make the position
if(isClicked){ //When Rendering
renderer.translate(lastClickPos.x, lastClickPos.y);
renderer.scale(scalingFactor);
renderer.translate(-lastClickPos.x, -lastClickPos.y);
}
void mouseClicked(){ //At the bottom
isClicked = !isClicked;
lastClickPos.set(mouseX, mouseY);
}

Related

How to create multicolor rasterized picture in processing

I followed this tutorial on Youtube, and I have successfully added colors to the black and white picture. However, my intention was to create a multi-color or gradient effect (like here or here) instead of switching colors when I move the cursor.
I very new at processing, and I have tried to play with the variable, with no success.
Here is the code snippet of the sketch:
`
PImage img;
void setup() {
size(598,336);
colorMode(HSB);
img = loadImage("picture-in-data-folder.jpg");
img.resize(598,336);
//ellipseMode(RADIUS);
frameRate(30);
}
void draw() {
background(255);
noStroke();
// fill(0);
float tiles = mouseX/10;
float tileSize = width/tiles;
// color section
fill(color(tiles, 255, 255));
tileSize++;
if (tiles > width / 2) {
tileSize = 0;
}
// end color section
translate(tileSize/2, tileSize/2);
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
color c = img.get(int(x*tileSize),int(y*tileSize));
float size = map(brightness(c), 0, 255, tileSize, 0);
ellipse(x*tileSize, y*tileSize, size, size);
// image(img, mouseX, mouseY);
}
}
}
I would be grateful if you had any hints, or if you could provide an advice.
Thanks.
Short answer: you need to put a fill() command inside the for loop.
Long answer:
Right now, your code is doing the following:
Define tiles based on mouseX
Set the fill color to (tiles, 255, 255)
Draw all the circles
I think what you want it to do is something like this:
Set the fill color to (21, 255, 255) (or whatever you want the first color to be)
draw the first circle
set the fill color to the next color in the gradient
draw the second circle
etc.
In order to do this, you need to put a command into the for loop which changes the fill color. Here is one way to do that:
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
color c = img.get(int(x*tileSize),int(y*tileSize));
float size = map(brightness(c), 0, 255, tileSize, 0);
fill(map(x, 0, tiles, 0, 255), 255, 255);
ellipse(x*tileSize, y*tileSize, size, size);
}
}
I just added that fill command as a function of x, but you can make it whatever you want. In order for it to be a gradient, it needs to vary somewhat with x or y.

Processing stop the animation

I have the following code (in Processing Software) which I want to stop the particles before they leave the screen…
So I dont know how to stop them near the edges of the screen…
Please advice…
Thanks
Particle [] pickles = new Particle [100];
void setup () {
size (500, 500);
smooth ();
for (int i=0; i<pickles.length; i++) {
pickles [i] = new Particle ();
}
}
void draw () {
background (0); //clear the background
for (int i=0; i<pickles.length; i++) {
pickles[i].update();
}
}
class Particle {
float x;
float y;
float velX ; // speed or velocity
float velY;
Particle () {
//x and y position to be in middle of screen
x = width/2;
y = height/2;
velX = random (-10, 10);
velY = random (-10, 10);
}
void update () {
x+=velX;
y+=velY;
fill (255);
ellipse (x, y, 10, 10);
}
}
You can check whether a particle is going outside the bounds of the screen by comparing its x and y values to the screen dimensions. For example:
if(x < 0){
// particle is off left edge of screen
}
else if(x > width){
// particle is off right edge of screen
}
When you detect one of these conditions, you can do a number of things:
Remove the particle from the array so it stops using system resources after it leaves the screen
Wrap the value around to the other side of the screen
Have it bounce off the edge by reversing its velocity
Which approach you take depends on exactly what you want to happen.
Shameless self-promotion: here is a tutorial on collision detection in Processing, which includes the above approaches.

javafx update pixels every frame

For a project I'm working on, I need to animate an ocean through salinity colormaps. Essentially, I have salinity data for n timesteps and I need to update the pixel colors to match the current salinity values.
Right now I'm going through every pixel on a canvas in a nested for-loop and, for each pixel, computing the current salinity value at that pixel and updating the pixel's color to match the salinity value.
I keep track of the frames with a AnimationTimer object. The problem is that I'm getting around 20 fps and the application is lagging on events like resizing the window.
private void drawHeatMap(int depth, float t) {
float meanSalt = model.getMeanSalinity(depth);
float stdSalt = model.getSalinityStdDev(depth);
Colorbar colorbar = new Colorbar(SALINITY_COLORMAP);
colorbar.setValueRange(meanSalt - stdSalt, meanSalt + (stdSalt / 2));
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
PixelWriter pixelWriter = graphicsContext.getPixelWriter();
for (int x = 0; x < canvas.getWidth(); ++x) {
for (int y = 0; y < canvas.getHeight(); ++y) {
GeoCoordinate coordinate = getMapCoordinates(x, y);
if (model.isOverWater(coordinate)) {
float salt = model.computeSalinity(coordinate, depth, t);
pixelWriter.setColor(x, y, colorbar.getColor(salt));
}
}
}
}
This is the code that runs every frame, but even a simpler version like the one below still runs at 30 fps.
private void drawHeatMapBenchmark(int depth, float t) {
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
PixelWriter pixelWriter = graphicsContext.getPixelWriter();
for (int x = 0; x < canvas.getWidth(); ++x) {
for (int y = 0; y < canvas.getHeight(); ++y) {
pixelWriter.setColor(x, y, Color.gray(Math.random()));
}
}
}
Anyone knows a better approach to this? I'm not familiar with computer graphics, thank you.
You are doing all this on the (single) JavaFX application thread. It might help if you first render your heat map into an image which you then draw onto the canvas because for your particular case it seems to be very simple to do all the image rendering on parallel threads which should speed up the whole process.

Making a moving circle disappear after being clicked on, Processing

I have written a program in which a UFO (in essence, a gray ellipse) appears from the center of the screen and flies to the edge. There is a laser that appears when the mouse is pressed, and disappears when the mouse is released. I want to make it so that the UFO disappears when the mouse clicks on it/the laser touches it.
I've made it as far as to make the UFO class and create variables that determine its movements and speed, and I was able to get the laser to appear directly on the cursor. I thought of making an 'if' statement to check if the cursor is within the radius (or diameter) of the UFO, and placing it inside of the for loop I created for the UFOs. However, I am not sure how to achieve the proper syntax to make it happen.
Note: You may need to wait a few seconds for the first circle to appear after you play the sketch.
UFO[] ufos = new UFO[3];
void setup() {
size(700, 700);
for (int j = 0; j < ufos.length; j++) {
ufos[j] = new UFO();
}
}
//UFO class
//Class setup ends on line 61
class UFO {
float a;
float b;
float c;
float sa;
float sb;
float d;
UFO() {
//declare float a/b/c value
a = random(-width/2, width/2);
b = random(-height/2, width/2);
c = random(width);
}
//UFO movement
void update() {
//float c will serve as a speed determinant of UFOs
c = c - 1;
if (c < 5) {
c = width;
}
}
//UFO setup
void show() {
//moving x/y coordinates of UFO
float sa = map(a / c, 0, 1, 0, width);
float sb = map(b / c, 0, 1, 0, height);
float d = map(c, 0, width, 50, 0);
//UFO drawing shapes
//ellipse is always sa (or sb) / c to allow UFO to appear
//as if it is moving through space
fill(200);
ellipse((sa / c), (sb / c), d + 5, d+5);
//Hides UFO way off the screen
//and replaces it with a black-filled ellipse until
//it forms into a full circle
//When radius d becomes 50, the UFO flies from the
//center of the screen to off of the screen
if (d < 50) {
fill(0);
ellipse(-5, -10, 90, 90);
sa = 10000;
sb = 10000;
}
}
}
void draw() {
//Background
background(0);
//Translated the screen so that the UFOs appear to fly from
//the center of the screen
translate(width/2, height/2);
//UFO draw loop, make UFO visible on screen
for (int j = 0; j < ufos.length; j++) {
ufos[j].update();
ufos[j].show();
//mouse-click laser
if (mousePressed == true) {
fill(200,0,0);
ellipse(mouseX - 352,mouseY - 347,50,50);
}
}
}
Like I said on the Happy Coding forum:
Basically, if your UFO is a series of circles, then you just need to use the dist() function to check whether the distance from the mouse to the center of the circle is less than the radius of the circle. If it is, then the mouse is inside the circle. Here's a small example:
float circleX = 50;
float circleY = 50;
float circleDiameter = 20;
boolean showCircle = true;
void draw(){
background(0);
if(showCircle){
ellipse(circleX, circleY, circleDiameter, circleDiameter);
}
}
void mousePressed(){
if(dist(mouseX, mouseY, circleX, circleY) < circleDiameter/2){
showCircle = false;
}
}
If your UFO is multiple circles, then you need to apply this logic to each circle. Please try something and post a small example like this one (not your whole sketch) if you get stuck. Good luck.

Drawing on Canvas and refreshing

I'm new to Android programming and now I'm trying to make a simple Sea Battle game for one person. Ships are places, player hits the field and see whether the shot hit or not.
Basically, the field looks like this:
The code is:
public void onDraw(Canvas canvas) {
if (getWidth() > getHeight()) {
rebro = getHeight();
} else {
rebro = getWidth(); // the smaller size of screen is "rebro"
}
rebro_piece = rebro / 10; // divide the screen by 10 (to get 10x10 field)
Paint background = new Paint();
background.setColor(getResources().getColor(R.color.game_background));
canvas.drawRect(0, 0, rebro, rebro, background); // draw background
Paint divider = new Paint();
divider.setColor(getResources().getColor(R.color.divider_black));
// drawing divider lines
for (int i=0; i<11; i++) {
canvas.drawLine(0, i*rebro_piece, rebro, i*rebro_piece, divider); // horizontal
canvas.drawLine(i*rebro_piece, 0, i*rebro_piece, rebro, divider); // vertical
}
canvas.drawLine(rebro-1, 0, rebro-1, rebro, divider);
}
That's how I make the "field."
In another class I have a method that collects numbers x and y of a 10×10 array that represents where the ships are placed. For debugging, I need to draw them on my field. Ship coordinates are retrieved in a cycle.
So I wrote a drawShip(int x, int y) method.
On Stack Overflow I've founded a question about "Why I can't paint outside onDraw()?" and I've changed my method to this:
public void drawShip(int x, int y) {
myX = x; //global
myY = y; //global
needToPaintShip = true; //boolean
invalidate(); // refreshing?
needToPaintShip = false;
}
Here needToPaintShip decides whether the redrawing of canvas is needed or not.
Also I've edited the onDraw(Canvas canvas) method:
if(needToPaintShip == true) {
Paint ship = new Paint();
ship.setColor(getResources().getColor(R.color.ship_color));
Log.d(TAG, "onDraw(): rebro_piece = " + rebro_piece + " , myX = "+ myX + " , myY = " + myY); // I only get the last coordinates!
Rect r = new Rect(myX*(rebro_piece),myY*rebro_piece, myX*(rebro_piece+1), myY*(rebro_piece+1));
canvas.drawRect(r, ship);
}
but the result is awful:
Guys, I'm desperate. How can I fix this and make "ships" be drawn on the field?
Why do you set needToPaintShip = false; after calling invalidate()? Don't you need to draw the ship again in subsequent frames?
Also, it seems like this item:
Rect r = new Rect(myX*(rebro_piece),myY*rebro_piece, myX*(rebro_piece+1), myY*(rebro_piece+1));
should probably be:
Rect r = new Rect(myX*(rebro_piece),myY*rebro_piece, (myX+1)*rebro_piece, (myY+1)*rebro_piece));
As for why the ship always appears in the bottom right corner, that depends on what you pass to drawShip(x,y), which isn't shown. Is it possible that you are passing pixel coordinates instead of something in the range [0-10)?

Categories