How to more + rotate 180 degree + move a group of shape using Processing? - rotation

I'm having this Processing issue where I'm trying to make my ariplane do a 180 turn-over right before it gets to the end of the drawing... It looks fairly simple but I just can't get it to work. Help would be much appreciated.
As I have other shapes (not showned in code below) I finally managed to use pushMatrix(); & popMatrix(); to only impact the airplane. Although rotating it brings me sideways.
int globalX = 0;
int globalY = 600;
int vitesse = 3;
void setup() {
size(800, 600);
rectMode(CENTER);
}
void draw() {
background(255);
pushMatrix();
bouger();
tourner();
dessinerAvion(globalX, globalY);
popMatrix();
}
void bouger() {
// Change the x location by vitesse
//globalX = globalX + vitesse;
globalY = globalY - vitesse;
}
void tourner() {
if (globalY < 0) {
for (int i = 10; i > 0; i = i-1) {
rotate(PI/3.0);
//rotate(radians(10));
}
for (int i = 10; i > 0; i = i-1) {
rotate(PI/3.0);
//rotate(radians(10));
vitesse = vitesse * -1;
}
}
}
void dessinerAvion(int x, int y) {
rectMode(CENTER);
// corps
fill(156,21,21);
ellipse(x + 250, y + 100, 50, 100);
rect(x + 250, y + 200, 50, 250,60);
// reacteur gauche
rect(x + 125, y + 225, 25, 50, 75);
// reacteur droite
rect(x + 375, y + 225, 25, 50, 75);
fill(210,14,14);
// aile gauche
quad(x + 75, y + 275, x + 75, y + 250, x + 225, y + 175, x + 225, y + 250);
// aile droite
quad(x + 425, y + 275, x + 425, y + 250, x + 275, y + 175, x + 275, y + 250);
fill(112,14,14);
// ailette gauche
triangle(x + 225, y + 290, x + 226, y + 310, x + 175, y + 310);
// ailette droites
triangle(x + 275, y + 290, x + 274, y + 310, x + 325, y + 310);
// aile verticale arrière
triangle(x + 250, y + 285, x + 248, y + 310, x + 252, y + 310);
}

Matrix are weird animals. They let you take a bunch of shapes and translate, rotate or scale them all at once, but only through playing with the coordinate system, which implies that the way you draw the shapes that you'll process will have a huge impact on how they react.
This is your plane on a neutral grid (by which I mean that globalX and globalY are 0):
Every square on this grid has a 20 pixels side. As you can see, your plane is far from the [0, 0] point, which is the start of the coordinate system it's drawn into. Also, when you modify it's coordinates, you're doing it through modifying it's coordinates, as we can see on this line of code:
void dessinerAvion(int x, int y)
The problem here is that applying a geometric transformation to an object with these coordinates can be counterintuitive. To simplify the matter, imagine the whole coordinate system that the plane use as if it was an image, like a png. The top left of the image is the [0, 0] point. When you change the plane's y coordinates for a higher number, your png image gets taller. If you change the x coordinate of the plane for a bigger number, the png image gets wider. You get the general idea.
When you apply a rotation to the plane, it's the entire 'png image', the entire coordinate system, that gets rotated. It's the same for any other transformation, too. Now, experimentally, look at what happens if I do this:
for(float i=0; i<20; i+=1) {
pushMatrix();
rotate(PI/i);
dessinerAvion(0, 0);
popMatrix();
}
As you can see, the plane isn't rotating on itself. It's the whole coordinate system that rotates, pivoting on the [0, 0] point, and it brings the plane with itself. That's the deal with matrix: they keep a lot of geometry waaay more simple, but you have to work accordingly.
Which brings me back to the previous point: you're updating the coordinates where you draw the plane. Then you use a matrix. Your updated plane will rotate in an unexpected way because you apply 2 different logic to your transformations: on one hand you modify coordinates (which is fine), while on the other you apply a transformation on a matrix (which is fine too). The issue is to mix those without acknowledging their differences.
Honestly, unless you have a specific thing in mind, you shouldn't mix those to move a single object.
The obvious solution to the first part of this issue would be to translate the plane instead of moving it's coordinates:
void draw() {
background(255);
bouger(); // updating coordinates, this doesn't need to be in the matrix block
pushMatrix();
//tourner();
translate(globalX, globalY);
dessinerAvion(0, 0); // zero here as the translation is doing that part of the job now
popMatrix();
}
Now you should see... well, you should see no difference at all. That's normal. The real difference is not in how the sketch appears, but how it's processed behind the scenes. Now your plane is a static object on which we apply geometric transformations.
Now to the other part of your problem: animating an object. Well, to be honest the animation part is quite easy, but there's a lot of work to do beforehand, more precisely to fix the plane's original coordinates so they are easier to manage. Let me explain:
If you look at the first image where I show the plane without any geometric transformation, you'll notice that the left wing starts at about ~75 pixels from the 0 point, and that the nose of the plane is ~45 pixels away from the 0 on the y axis. Currently, that's the rotation's anchor point. If you want to rotate the plane in a gracious manner, you'll have to fix this. There are many ways to proceed, follow me.
Take notes, because for some reason very few people will explain in simple words how to rotate on an anchor point using a matrix.
To rotate a shape on a specific anchor point, you have to proceed as follow:
pushMatrix
translate to the shape's destination
rotate
draw the shape using [0, 0] as it's anchor point
popMatrix
So if you want to rotate your plane on itself, thus deciding on an angle then moving it to it's destination before you draw the plane, you act as follow:
void draw() {
background(255);
// grid
stroke(0);
for (int i=0; i<height; i+=20) {
line(0, i, width, i);
}
for (int i=0; i<width; i+=20) {
line(i, 0, i, height);
}
pushMatrix();
translate(200, 200); // for the demonstration only
globalAngle--;
rotate(radians(globalAngle)); // I'm lazy and I don't want to do this in radians so I use degrees and convert them
dessinerAvion(-250, -200); // the center of the plane is the [250, 200] point. That's my anchor point.
popMatrix();
}
Which gives us this result:
Now, using the first translate, you can draw the plane wherever you want, and you can use the rotation to make it face any direction you want it to face.
Using these, it would be real easy to make the plane cross the screen back and forth:
void draw() {
background(255);
drawGrid();
// implementing the "u-turn"
globalY -= vitesse;
if (globalY < -200) {
globalX -= 200;
globalAngle = 180;
vitesse = -vitesse;
}
// drawing the sketch
pushMatrix();
translate(globalX, globalY);
rotate(radians(globalAngle));
dessinerAvion(-250, -200);
popMatrix();
}
Now, if we want to watch the plane turn, we have several options. You can rotate the plane frame by frame while plotting it's course. That's a good option. But I'm lazy, as previously stated, so instead I'll move the anchor point somewhere left of the plane and use it to rotate around it in a gracious manner:
void draw() {
background(255);
drawGrid();
pushMatrix();
translate(globalX, globalY);
tourner();
bouger();
dessinerAvion(0, -200); // this coordinate sets my anchor point
popMatrix();
}
void tourner() {
if (globalY < 300) { // starting the u-turn
vitesse = 0; // The plane won't move while it's rotating... except for the rotation itself
globalAngle--; // rotating counterclockwise 1 degree per frame
if (globalAngle<-180) { // u-turn completed
globalAngle = -180;
vitesse = -3;
}
}
rotate(radians(globalAngle));
}
I think that does it. I hope this helps. I'll lurk around in case you have questions about this answer.
Have fun!

Related

Let PShapes in an array rotate on its own axis in Processing

I have this code that basically reads each pixel of an image and redraws it with different shapes. All shapes will get faded in using a sin() wave.
Now I want to rotate every "Pixelshape" around its own axis (shapeMode(CENTER)) while they are faded in and the translate function gives me a headache in this complex way.
Here is the code so far:
void setup() {
size(1080, 1350);
shapeMode(CENTER);
img = loadImage("loremipsum.png");
…
}
void draw() {
background(123);
for (int gridX = 0; gridX < img.width; gridX++) {
for (int gridY = 0; gridY < img.height; gridY++) {
// grid position + tile size
float tileWidth = width / (float)img.width;
float tileHeight = height / (float)img.height;
float posX = tileWidth*gridX;
float posY = tileHeight*gridY;
// get current color
color c = img.pixels[gridY*img.width+gridX];
// greyscale conversion
int greyscale = round(red(c)*0.222+green(c)*0.707+blue(c)*0.071);
int gradientToIndex = round(map(greyscale, 0, 255, 0, shapeCount-1));
//FADEIN
float wave = map(sin(radians(frameCount*4)), -1, 1, 0, 2);
//translate(HEADACHE);
rotate(radians(wave));
shape(shapes[gradientToIndex], posX, posY, tileWidth * wave, tileHeight * wave);
}
}
I have tried many calculations but it just lets my sketch explode.
One that worked in another sketch where I tried basically the same but just in loop was (equivalent written):
translate(posX + tileWidth/2, posY + tileHeight/2);
I think I just don't get the matrix right? How can I translate them to its meant place?
Thank you very much #Rabbid76 – at first I just pasted in your idea and it went of crazy – then I added pushMatrix(); and popMatrix(); – turned out your translate(); code was in fact right!
Then I had to change the x and y location where every shape is drawn to 0,0,
And this is it! Now it works!
See the code here:
float wave = map(sin(radians(frameCount*4)), -1, 1, 0, 2);
pushMatrix();
translate(posX + tileWidth/2, posY + tileHeight/2);
rotate(radians(wave*180));
shape(shapes[gradientToIndex], 0, 0, tileWidth*wave , tileHeight*wave );
popMatrix();
PERFECT! Thank you so much!
rotate defines a rotation matrix and multiplies the current matrix by the rotation matrix. rotate therefore causes a rotation by (0, 0).
You have to center the rectangle around (0, 0), rotate it and move the rotated rectangle to the desired position with translate.
Since translate and rotate multiplies the current matrix by a new matrix, you must store and restore the matrix by pushMatrix() respectively popMatrix().
The center of a tile is (posX + tileWidth/2, posY + tileHeight/2):
pushMatrix();
translate(posX + tileWidth/2, posY + tileHeight/2);
rotate(radians(wave));
shape(shapes[gradientToIndex],
-tileWidth*wave/2, -tileHeight*wave/2,
tileWidth * wave, tileHeight * wave);
popMatrix();

Different Processing rendering between native and online sketch

I get different results when running this sample with Processing directly, and with Processing.js in a browser. Why?
I was happy about my result and wanted to share it on open Processing, but the rendering was totally different and I don't see why. Below is a minimal working example.
/* Program that rotates a triange and draws an ellipse when the third vertex is on top of the screen*/
float y = 3*height/2;
float x = 3*width/2;
float previous_1 = 0.0;
float previous_2 = 0.0;
float current;
float angle = 0.0;
void setup() {
size(1100, 500);
}
void draw() {
fill(0, 30);
// rotate triangle
angle = angle - 0.02;
translate(x, y);
rotate(angle);
// display triangle
triangle(-50, -50, -30, 30, -90, -60);
// detect whether third vertex is on top by comparing its 3 successive positions
current = screenY(-90, -60); // current position of the third vertex
if (previous_1 < previous_2 && previous_1 < current) {
// draw ellipse at the extrema position
fill(128, 9, 9);
ellipse(-90, -60, 7, 10);
}
// update the 2 previous positions of the third vertex
previous_2 = previous_1;
previous_1 = current;
}
In processing, the ellipse is drawn when a triangle vertex is on top, which is my goal.
In online sketching, the ellipse is drawn during the whole time :/
In order to get the same results online as you get by running Processing locally you will need to specify the rendering mode as 3d when calling size
For example:
void setup() {
size(1100, 500, P3D);
}
You will also need to specify the z coordinate in the call to screenY()
current = screenY(-90, -60, 0);
With these two changes you should get the same results online as you get running locally.
Online:
Triangle Ellipse Example
Local:
The problem lies in the screenY function. Print out the current variable in your processing sketch locally and online. In OpenProcessing, the variable current grows quickly above multiple thousands, while it stays between 0 and ~260 locally.
It seems like OpenProcessing has a bug inside this function.
To fix this however, I would recommend you to register differently when you drew a triangle at the top of the circle, for example by using your angle variable:
// Calculate angle and modulo it by 2 * PI
angle = (angle - 0.02) % (2 * PI);
// If the sketch has made a full revolution
if (previous_1 < previous_2 && previous_1 < angle) {
// draw ellipse at the extrema position
fill(128, 9, 9);
ellipse(-90, -60, 7, 10);
}
// update the 2 previous angles of the third vertex
previous_2 = previous_1;
previous_1 = angle;
However, because of how you draw the triangles, the ellipse is at an angle of about PI / 3. To fix this, one option would be to rotate the screen by angle + PI / 3 like so:
rotate(angle + PI / 3);
You might have to experiment with the angle offset a bit more to draw the ellipse perfectly at the top of the circle.

P3D camera orientation

I've got a big sphere. There is a red dot that moves around the sphere. I want to follow that red dot as it moves around to sphere. So my camera has to move with the red dot. But there is a problem. Right now, what I'm experiencing is what is shown in exhibit B. I want my animation to realize the point of view shown in exhibit A.
Here is the code I have so far. I think it's pretty simple. I have 3 variables that control where my eye is, and I have 3 variables that control where the target is. The red dot is also located at the target position. I added 2 planes in x-y which helped me not get too confused as the thing was spinning.
Here is a fiddle:
https://jsfiddle.net/da8nza6y/
float radius = 1000;
float view_elevation = 1500;
float target_elevation = 300;
float x_eye;
float y_eye;
float z_eye;
float x_aim;
float y_aim;
float z_aim;
float h;
float theta;
void setup() {
size(600, 600, P3D);
theta = 0;
h = 30;
}
void draw() {
theta += 0.5;
theta = theta%360;
x_eye = (radius+view_elevation)*cos(theta*PI/180);
y_eye = 0;
z_eye = (radius+view_elevation)*sin(theta*PI/180);
x_aim = (radius+target_elevation)*cos((theta+h)*PI/180);
y_aim = 0;
z_aim = (radius+target_elevation)*sin((theta+h)*PI/180);
camera(x_eye, y_eye, z_eye, x_aim, y_aim, z_aim, 0, 0, -1);
background(255);
// the red dot
pushMatrix();
translate(x_aim, y_aim, z_aim);
fill(255, 0, 0, 120);
noStroke();
sphere(10);
popMatrix();
// the big sphere
noStroke();
fill(205, 230, 255);
lights();
sphere(radius);
// the orange plane
pushMatrix();
translate(0, 0, 10);
fill(255, 180, 0, 120);
rect(-2000, -2000, 4000, 4000);
popMatrix();
// the green plane
pushMatrix();
translate(0, 0, -10);
fill(0, 180, 0, 120);
rect(-2000, -2000, 4000, 4000);
popMatrix();
}
So the pickle is that, it seems like the moment the red dot (whose location in the x-z plane is given by the angle (theta+h) and distance (radius+target_elevation) from the origin) crosses the x-y plane, everything gets flipped upside-down and backwards.
Now, I have tried to control the last 3 variables in the camera() function but I'm getting confused. The documentation for the function is here:
https://processing.org/reference/camera_.html
Can anyone see a solution to this problem?
Also, I'm sure I could just rotate the sphere (which I can do) and not have these problems, but I'm sure where I'm going with this animation and I feel like there will be things to come that will be easier with this method. Though I could be mistaken.
I believe I've solved my own problem.
I've added the following lines in draw, before calling the camera() function:
if ((x_eye- x_aim) < 0) {
z_orientation = 1;
} else {
z_orientation = -1;
}
I noticed that it wasn't (theta+h) that was triggering the flip, but the relative positions of the view and target.
Here is an updated fiddle:
https://jsfiddle.net/da8nza6y/1/

Calculate ellipse size in relation to distance from center point

I want to achieve a slow fade in size on every collapse into itself. In other words, when the circle is at its biggest, the ellipses will be at the largest in size and conversely the opposite for the retraction. So far I am trying to achieve this affect by remapping the cSize from the distance of the center point, but somewhere along the way something is going wrong. At the moment I am getting a slow transition from small to large in ellipse size, but the inner ellipses are noticeably larger. I want an equal distribution of size amongst all ellipses in relation to center point distance.
I've simplified the code down to 4 ellipses rather than an array of rows of ellipses in order to hopefully simplify this example. This is done in the for (int x = -50; x <= 50; x+=100).
I've seen one or two examples that slightly does what I want, but is more or less static. This example is kind of similar because the ellipse size gets smaller or larger in relation to the mouse position
Distance2D
Here is an additional diagram of the grid of ellipses I am trying to create, In addition, I am trying to scale that "square grid" of ellipses by a center point.
Multiple ellipses + Scale by center
Any pointers?
float cSize;
float shrinkOrGrow;
void setup() {
size(640, 640);
noStroke();
smooth();
fill(255);
}
void draw() {
background(#202020);
translate(width/2, height/2);
if (cSize > 10) {
shrinkOrGrow = 0;
} else if (cSize < 1 ) {
shrinkOrGrow = 1;
}
if (shrinkOrGrow == 1) {
cSize += .1;
} else if (shrinkOrGrow == 0) {
cSize -= .1;
}
for (int x = -50; x <= 50; x+=100) {
for (int y = -50; y <= 50; y+=100) {
float d = dist(x, y, 0, 0);
float fromCenter = map(cSize, 0, d, 1, 10);
pushMatrix();
translate(x, y);
rotate(radians(d + frameCount));
ellipse(x, y, fromCenter, fromCenter);
popMatrix();
}
}
}
The values you're passing into the map() function don't make a lot of sense to me:
float fromCenter = map(cSize, 0, d, 1, 100);
The cSize variable bounces from 1 to 10 independent of anything else. The d variable is the distance of each ellipse to the center of the circle, but that's going to be static for each one since you're using the rotate() function to "move" the circle, which never actually moves. That's based only on the frameCount variable, which you never use to calculate the size of your ellipses.
In other words, the position of the ellipses and their size are completely unrelated in your code.
You need to refactor your code so that the size is based on the distance. I see two main options for doing this:
Option 1: Right now you're moving the circles on screen using the translate() and rotate() functions. You could think of this as the camera moving, not the ellipses moving. So if you want to base the size of the ellipse on its distance from some point, you have to get the distance of the transformed point, not the original point.
Luckily, Processing gives you the screenX() and screenY() functions for figuring out where a point will be after you transform it.
Here's an example of how you might use it:
for (int x = -50; x <= 50; x+=100) {
for (int y = -50; y <= 50; y+=100) {
pushMatrix();
//transform the point
//in other words, move the camera
translate(x, y);
rotate(radians(frameCount));
//get the position of the transformed point on the screen
float screenX = screenX(x, y);
float screenY = screenY(x, y);
//get the distance of that position from the center
float distanceFromCenter = dist(screenX, screenY, width/2, height/2);
//use that distance to create a diameter
float diameter = 141 - distanceFromCenter;
//draw the ellipse using that diameter
ellipse(x, y, diameter, diameter);
popMatrix();
}
}
Option 2: Stop using translate() and rotate(), and use the positions of the ellipses directly.
You might create a class that encapsulates everything you need to move and draw an ellipse. Then just create instances of that class and iterate over them. You'd need some basic trig to figure out the positions, but you could then use them directly.
Here's a little example of doing it that way:
ArrayList<RotatingEllipse> ellipses = new ArrayList<RotatingEllipse>();
void setup() {
size(500, 500);
ellipses.add(new RotatingEllipse(width*.25, height*.25));
ellipses.add(new RotatingEllipse(width*.75, height*.25));
ellipses.add(new RotatingEllipse(width*.75, height*.75));
ellipses.add(new RotatingEllipse(width*.25, height*.75));
}
void draw() {
background(0);
for (RotatingEllipse e : ellipses) {
e.stepAndDraw();
}
}
void mouseClicked() {
ellipses.add(new RotatingEllipse(mouseX, mouseY));
}
void mouseDragged() {
ellipses.add(new RotatingEllipse(mouseX, mouseY));
}
class RotatingEllipse {
float rotateAroundX;
float rotateAroundY;
float distanceFromRotatingPoint;
float angle;
public RotatingEllipse(float startX, float startY) {
rotateAroundX = (width/2 + startX)/2;
rotateAroundY = (height/2 + startY)/2;
distanceFromRotatingPoint = dist(startX, startY, rotateAroundX, rotateAroundY);
angle = atan2(startY-height/2, startX-width/2);
}
public void stepAndDraw() {
angle += PI/64;
float x = rotateAroundX + cos(angle)*distanceFromRotatingPoint;
float y = rotateAroundY + sin(angle)*distanceFromRotatingPoint;
float distance = dist(x, y, width/2, height/2);
float diameter = 50*(500-distance)/500;
ellipse(x, y, diameter, diameter);
}
}
Try clicking or dragging in this example. User interaction makes more sense to me using this approach, but which option you choose really depends on what fits inside your head the best.

How to draw string objects at an angle in Processing?

The code below draws a spiral using objects from a string array. Everything is fine, except that I would like the text objects to be drawn at a roughly 45 degree angle at each instance (based on the current x, y coordinates in the code below) rather than being drawn horizontally (when the text is horizontally drawn, it naturally overlaps with other text at concentrated points along the top & bottom of the curve). I researched some methods, but I'm still very new to all of this, and potential solutions have all evaded me.
String example = "";
String[] wordSet = split(example, " ");
float x, y;
float angle = 0;
float radiusSpiralLine = 10;
size (800, 800);
translate(width/2, height/2);
background(#ffffff);
smooth();
fill(0);
for (int i = 0; i < wordSet.length; i++) {
angle += .05;
radiusSpiralLine += .5;
x = cos(angle) * radiusSpiralLine;
y = sin(angle) * radiusSpiralLine;
textSize(9);
text(wordSet[i], x, y);
}
Here is tutorial to very similar problem. In basic you need to store projection matrix by pushMatrix() then translate and rotate according to position of letter on curve and then restore matrix by popMatrix(). I don't know how exactly do you want to rotate you text but just fold round your text() function like this maybe it will help you:
pushMatrix();
translate(x, y);
rotate(angle);
text(wordSet[i], 0, 0);
popMatrix();
First, you should start getting in the habit of wrapping code in the setup() and draw() functions. Since you're drawing a static image you don't need the draw() function, but I think it's good practice to have those two.
Now, what you are doing now is simply translating the words by a very small amount. Do the math:
x = cos(angle) * radiusSpiralLine; //cos(.05)*.5 = .499
y = sin(angle) * radiusSpiralLine; //sin(.05)*.5 = .024
That means they move less than a pixel, and they're not rotating at all.
What you need is your good ol' friend, the rotate() function.
Let's re-write code:
String example = "These are a bunch of words going around!";
String[] wordSet = split(example, " ");
float x, y;
float angle = 0;
void setup() {
size (800, 800);
background(#ffffff);
smooth();
fill(0);
pushMatrix();
translate(width/2, height/2); //Translate when you need to translate, not before
for (int i = 0; i < wordSet.length; i++) {
angle = PI/5; //Our good friends, radians
textSize(20); //What is this, text for ants? Change to 20
rotate(angle);
text(wordSet[i], 20, 0);
}
popMatrix();
}
void draw() {
}
First notice, the setup() and draw(). I like them there. It looks nicer, I think.
A couple of important things to note.
The effects of rotate() and translate() are on the canvas are cumulative.
We could have had the same effect in different ways:
for (int i = 0; i < wordSet.length; i++) {
angle = PI/5;
textSize(20);
rotate(angle); //always rotating by PI/5 ON TOP of previous rotation
text(wordSet[i], 20, 0);
}
//Everything from now on will still be rotated, we don't want that!
Slightly better, but not there yet:
for (int i = 0; i < wordSet.length; i++) {
angle += PI/5; //constantly increasing the angle
textSize(20);
pushMatrix(); //push a new canvas on top of everything
rotate(angle); //rotate by angle (which increases every loop)
text(wordSet[i], 20, 0);
popMatrix(); //pop the rotated canvas out, go back to original canvas
} //Things won't be rotated, but they'll still be translated, since translate() is outside of pushMatrix and popMatrix
Hope this helps.

Resources