P3D camera orientation - processing

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/

Related

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

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!

How to manually apply matrix stack to coordinates in processing

In processing, when you apply a matrix transformation, you can draw on your canvas without worrying of the "true" position of your x y coordinate.
I thought that by the same logic, I could copy a section of the canvas by using ParentApplet.get(x, y, width, height) and that it would automatically shift the x and y, but it does not, it uses the coordinates as raw inputs without applying the matrix stack to it.
So the easiest way I see to deal with the problem would be to manually apply the matrix stack to my x, y, width, height values and using the results as input of get(). But I cannot find such a function, does one exist ?
EDIT : As requested, Here's an example of my problem
So the objective here is to draw a simple shape, copy it and paste it. Without translate, there is no problem:
void settings(){
size(500, 500);
}
void draw() {
background(255);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// copy rectangle and paste it elsewhere
PImage img = get(0, 0, 101, 101);
image(img, 200, 200);
}
Now if I applied a translate matrix before drawing the shape, I wish that I could use the same get() code to copy the exact same drawing:
void settings(){
size(500, 500);
}
void draw() {
background(255);
pushMatrix();
translate(10, 10);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// copy rectangle and paste it elsewhere
PImage img = get(0, 0, 101, 101);
image(img, 200, 200);
popMatrix();
}
But it doesn't work that way, The get(0, 0, ..) doesn't use the current transformation matrix to copy pixels from origin (10, 10):
Can you please provide a few more details.
It is possible to manipulate coordinate systems using pushMatrix()/PopMatrix() and you can go further and manually multiply matrices and vectors.
The part that is confusing is that you're calling get(x,y,width,height) but no showing how you render the PImage section. It's hard to guess the matrix stack you're mentioning. Can you post an example snippet ?
If you render it at the same x,y you call get() with it should render with the same x,y shift:
size(640, 360);
noFill();
strokeWeight(9);
PImage placeholderForPGraphics = loadImage("https://processing.org/examples/moonwalk.jpg");
image(placeholderForPGraphics, 0, 0);
int x = 420;
int y = 120;
int w = 32;
int h = 48;
// visualise region of interest
rect(x, y, w, h);
// grab the section sub PImage
PImage section = placeholderForPGraphics.get(x, y, w, h);
//filter the section to make it really standout
section.filter(THRESHOLD);
// display section at same location
image(section, x, y);
Regarding the matrix stack, you can call getMatrix() which will return a PMatrix2D if you're in 2D mode (otherwise a PMatrix3D). This is a copy of the current matrix stack at the state you've called it (any prior operations will be "baked" into this one).
For example:
PMatrix m = g.getMatrix();
printArray(m.get(new float[]{}));
(g.printMatrix() should be easier to print to console, but you need to call getMatrix() if you need an instance to manipulate)
Where g is your PGraphics instance.
You can then manipulate it as you like:
m.translate(10, 20);
m.rotate(radians(30));
m.scale(1.5);
Remember to call applyMatrix() it when you're done:
g.applyMatrix(m);
Trivial as it may be I hope this modified version of the above example illustrates the idea:
size(640, 360);
noFill();
strokeWeight(9);
// get the current transformation matrix
PMatrix m = g.getMatrix();
// print to console
println("before");
g.printMatrix();
// modify it
m.translate(160, 90);
m.scale(0.5);
// apply it
g.applyMatrix(m);
// print applied matrix
println("after");
g.printMatrix();
PImage placeholderForPGraphics = loadImage("https://processing.org/examples/moonwalk.jpg");
image(placeholderForPGraphics, 0, 0);
int x = 420;
int y = 120;
int w = 32;
int h = 48;
// visualise region of interest
rect(x, y, w, h);
// grab the section sub PImage
PImage section = placeholderForPGraphics.get(x, y, w, h);
//filter the section to make it really standout
section.filter(THRESHOLD);
// display section at same location
image(section, x, y);
Here's another example making a basic into PGraphics using matrix transformations:
void setup(){
size(360, 360);
// draw something manipulating the coordinate system
PGraphics pg = createGraphics(360, 360);
pg.beginDraw();
pg.background(0);
pg.noFill();
pg.stroke(255, 128);
pg.strokeWeight(4.5);
pg.rectMode(CENTER);
pg.translate(180,180);
for(int i = 0 ; i < 72; i++){
pg.rotate(radians(5));
pg.scale(0.95);
//pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
polygon(6, 180, pg);
}
pg.endDraw();
// render PGraphics
image(pg, 0, 0);
}
This is overkill: the same effect could have been drawn much simpler, however the focus in on calling get() and using transformation matrices. Here a modified iteration showing the same principle with get(x,y,w,h), then image(section,x,y):
void setup(){
size(360, 360);
// draw something manipulating the coordinate system
PGraphics pg = createGraphics(360, 360);
pg.beginDraw();
pg.background(0);
pg.noFill();
pg.stroke(255, 128);
pg.strokeWeight(4.5);
pg.rectMode(CENTER);
pg.translate(180,180);
for(int i = 0 ; i < 72; i++){
pg.rotate(radians(5));
pg.scale(0.95);
//pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
polygon(6, 180, pg);
}
pg.endDraw();
// render PGraphics
image(pg, 0, 0);
// take a section of PGraphics instance
int w = 180;
int h = 180;
int x = (pg.width - w) / 2;
int y = (pg.height - h) / 2;
PImage section = pg.get(x, y, w, h);
// filter section to emphasise
section.filter(INVERT);
// render section at sampled location
image(section, x, y);
popMatrix();
}
void polygon(int sides, float radius, PGraphics pg){
float angleIncrement = TWO_PI / sides;
pg.beginShape();
for(int i = 0 ; i <= sides; i++){
float angle = (angleIncrement * i) + HALF_PI;
pg.vertex(cos(angle) * radius, sin(angle) * radius);
}
pg.endShape();
}
Here's a final iteration re-applying the last transformation matrix in an isolated coordinate space (using push/pop matrix calls):
void setup(){
size(360, 360);
// draw something manipulating the coordinate system
PGraphics pg = createGraphics(360, 360);
pg.beginDraw();
pg.background(0);
pg.noFill();
pg.stroke(255, 128);
pg.strokeWeight(4.5);
pg.rectMode(CENTER);
pg.translate(180,180);
for(int i = 0 ; i < 72; i++){
pg.rotate(radians(5));
pg.scale(0.95);
//pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
polygon(6, 180, pg);
}
pg.endDraw();
// render PGraphics
image(pg, 0, 0);
// take a section of PGraphics instance
int w = 180;
int h = 180;
int x = (pg.width - w) / 2;
int y = (pg.height - h) / 2;
PImage section = pg.get(x, y, w, h);
// filter section to emphasise
section.filter(INVERT);
// print last state of the transformation matrix
pg.printMatrix();
// get the last matrix state
PMatrix m = pg.getMatrix();
// isolate coordinate space
pushMatrix();
//apply last PGraphics matrix
applyMatrix(m);
// render section at sampled location
image(section, x, y);
popMatrix();
save("state3.png");
}
void polygon(int sides, float radius, PGraphics pg){
float angleIncrement = TWO_PI / sides;
pg.beginShape();
for(int i = 0 ; i <= sides; i++){
float angle = (angleIncrement * i) + HALF_PI;
pg.vertex(cos(angle) * radius, sin(angle) * radius);
}
pg.endShape();
}
This is an extreme example, as 0.95 downscale is applied 72 times, hence a very small image is rendered. Also notice the rotation is incremented.
Update Based on your update snippet it seems the confusion is around pushMatrix() and get().
In your scenario, pushMatrix()/translate() will offset the local coordinate sytem: that is where elements are drawn.
get() is called globally and uses absolute coordinates.
If you're only using translation, you can simply store the translation coordinates and re-use them to sample from the same location:
int sampleX = 10;
int sampleY = 10;
void settings(){
size(500, 500);
}
void draw() {
background(255);
pushMatrix();
translate(sampleX, sampleY);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// copy rectangle and paste it elsewhere
PImage img = get(sampleX, sampleY, 101, 101);
image(img, 200, 200);
popMatrix();
}
Update
Here are a couple more examples on how to compute, rather than hard code the translation value:
void settings(){
size(500, 500);
}
void setup() {
background(255);
pushMatrix();
translate(10, 10);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// local to global coordinate conversion using PMatrix
// g is the global PGraphics instance every PApplet (sketch) uses
PMatrix m = g.getMatrix();
printArray(m.get(null));
// the point in local coordinate system
PVector local = new PVector(0,0);
// multiply local point by transformation matrix to get global point
// we pass in null to get a new PVector instance: you can make this more efficient by allocating a single PVector ad re-using it instead of this basic demo
PVector global = m.mult(local,null);
// copy rectangle and paste it elsewhere
println("local",local,"->global",global);
PImage img = get((int)global.x, (int)global.y, 101, 101);
image(img, 200, 200);
popMatrix();
}
To calculate the position of a vector based on a transformation matrix, simply multiply the vector by that matrix. Very roughly speaking what's what happens with push/pop matrix (a transformation matrix is used for each push/pop stack, which is then multiplied all the way up the global coordinate system). (Notice the comment on efficienty/pre-allocating matrices and vectors as well).
This will be more verbose in terms of code and may need a bit of planning if you're using a lot of nested transformations, however you have finer control of which transformations you choose to use.
A simpler solution may be to switch to the P3D OpenGL renderer which allows you use screenX(), screenY() to do this conversion. (Also checkout modelX()/modelY())
void settings(){
size(500, 500, P3D);
}
void draw() {
background(255);
pushMatrix();
translate(10, 10);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// local to global coordinate conversion using modelX,modelY
float x = screenX(0, 0, 0);
float y = screenY(0, 0, 0);
println(x,y);
PImage img = get((int)x, (int)y, 101, 101);
image(img, 200, 200);
popMatrix();
}
Bare in mind that you want to grab a rectangle which simply has translation applied. Since get() won't take rotation/scale into account, for more complex cases you may want to convert local to global coordinates of not just the top left point, but also the bottom right one with an offset. The idea is to compute the larger bounding box (with no rotation) around the transformed box so when you call get() the whole area of interest is returned (not just a clipped section).

How can I adjust this code to include vertexes in the for loops?

//Study and use beginShape() and endShape() to draw 3 separate sketches. Each sketch must contain vertices ( vertex() ). tools you will need: beginShape(), endShape(), vertex(), for loop.
//We are supposed to be making Moire patterns. I have gotten the desired result with the code below but it does not include vertexes as the assignment asks. How can I edit adjust this code to include vertices but give the same output?
var theta = 0.0;
var circWidthMultiplier = 17.5;
var circHeightMultiplier = 12.5;
var rectWidthMultiplier = 12.5;
var rectHeightMultiplier =17.5;
var rotationSpeed = 0.005;
function setup() {
createCanvas(windowWidth,windowHeight);
rectMode(CENTER);
}
function draw() {
background(100);
noFill();
push();
beginShape();
translate(width/2, height/2);
for(var i =0; i < 50; i+=5){
ellipse(0, 0,
i*rectWidthMultiplier,
i*rectHeightMultiplier);
}
endShape();
pop();
push();
beginShape();
translate(width/2, height/2);
rotate(theta);
for(var i =0; i < 50; i+=5){
ellipse(0, 0,
i*circWidthMultiplier,
i*circHeightMultiplier);
}
endShape();
pop();
beginShape();
translate(width/2, height/2);
for(var i =0; i < 50; i+=2){
rect(0, 0,
i*circWidthMultiplier,
i*circHeightMultiplier);
}
endShape();
pop();
theta += rotationSpeed;
}
Right now you're using functions like ellipse() and rect() to draw your shapes. You could use beginShape(), vertex(), and endShape() to draw your shapes instead.
Start with replacing the rect() function. You already know the position and size of the rectangle. So it should be pretty easy to replace that with four calls to the vertex() function.
The circles are a little bit trickier, but still doable. You'd need to use basic trigonometry to figure out the vertexes around the circle. Google is your friend here.
But honestly, this seems a little bit pointless. Why bother using vertex() to draw shapes that Processing already draws for you? You might want to check with your instructor to make sure that you're understanding the assignment correctly.

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.

“Rotate” and “translate” in processing give me headaches

As a small homework to get into Processing, I had to write some code to get the following:
Using
public void setup() {
size(300,200);
noFill();
rect(100, 20, 40, 80);
ellipseMode(CENTER);
fill(#000000);
ellipse(width/2, height/2, 5,5);
noFill();
translate(width/2, height/2);
rotate(radians(65));
rect(-20, -40, 40, 80);
}
public void draw() {
}
this worked very good, so far. But I don’t like that I had to change the coordinates inside of the bottom rect instruction in order to get the rotation right. I know that by rotating you don’t rotate single elements but in fact the whole coordinate system.
What I don’t know is which values to put into the translate instruction to have the output be like in the picture above while also still using the same coordinates within the rect command.
The task is already done with the code I used, I just don’t like it too much. So this isn’t mere asking for somebody else doing my homework but pure interest.
EDIT: More generalised attempt of a question: How do I know which values to pit into translate before rotate to get whatever result I want? Is there a way to calculate them? For sure, it’s not just trying out, is it?
A lot of the confusion in processing is the coordinate system. In Processing the origin (0,0) is at the top left of the screen and only positive coordinates display on the screen. The very common workaround for this is to call:
translate(width/2, height/2);
at the beginning of your void draw() method. That way 0,0 is now at the center of the sketch, and any subsequent methods called such as rotate(radians(65)) will take action from the center of the sketch.
This is also good because sketches that use the P3D or OPENGL renderer often call translate to change the coordinate system into something that is easier to use. For example an object at 0,0 or 0,0,0 is at the center and it makes it easier to orbit the camera around the object or have the object rotate around its center.
another popular way of drawing objects would be to set the origin as above and instead of giving the coordinates of each object, i.e. rect(-100, -50, 50, 50) is to use popMatrix() and pushMatrix before a translate before drawing each object at 0,0 as illustrated below:
translate(width/2, height/2);
pushMatrix();
translate(-100, -50);
rect(0,0,50,50);
popMatrix();
This is a good approach to use in a 2d renderer if you think you might move to 3d renderer eventually, because you can easily replace rect() with box() or sphere() or create your own method or object that draws geometry assuming the origin is at 0,0.
If you replace the x and y coordinates with variables or iterate through an array in a for loop it becomes very easy to draw hundreds or thousands of shapes in either 2d or 3d with minimal effort and minimal rewriting of the code.
update: added clarification for the original poster, per their comment.
I have changed the problem slightly to show you how I would approach this. I am showing the translate / rotate method I described above using push and pop matrix. I am just guessing at the values, but if you want something pixel accurate you can take measurements in an image editing program like photoshop or preview.
translate(180, 150);
rect(0, 0, 180, 80);
translate(185, 170);
rotate(radians(65));
rect(0, 0, 180, 80);
translate(180, 250);
ellipse(0, 0, 8, 8);
Putting it all together with pushMatrix() and popMatrix().
void setup(){
size(400,400);
}
void draw(){
// rect1
noFill();
pushMatrix();
translate(180, 150);
rect(0, 0, 180, 80);
popMatrix();
// rect2
pushMatrix();
translate(185, 170);
rotate(radians(65));
rect(0, 0, 180, 80);
popMatrix();
// ellipse
fill(0);
pushMatrix();
translate(180, 250);
ellipse(0, 0, 8, 8);
popMatrix();
}
in my particular example, know how to translate and rotate to get the
result in the picture without changing the rectangle coordinates?
Well, you need to draw at origin to use rotate properly, so you are just dealing with the origin of the rect... As it is in your code, the default, the origin is the upper left corner, so you made an off set (from (0,0)) to draw it's centre, not the corner, at coordinate(0,0), then rotate by the middle. Well done. It is not coincidence that the values you found was minus half rect width(-20) and minus half rect height(-40). If you change to rectMode(CENTER) than you can just draw at (0,0). The same applies to translate, you can just translate to the desired point, the center of the screen, where there is the ellipse. What is done using half width and half height...
look:
public void setup() {
size(300, 200);
smooth();
ellipseMode(CENTER);
rectMode(CENTER);
noFill();
// here converting the coordinates to work with CENTER mode
//could have changed the rect mode just after this
// but i kept as an illustration
int rwidth = 40;
int rheight = 80;
int xpos = 100 + rwidth/2;
int ypos = height/2 - rheight/2 ;
rect(xpos, ypos, rwidth, rheight);
fill(#000000);
ellipse(width/2, height/2, 5, 5);
noFill();
pushMatrix();
translate(width/2, height/2);
//draw at origin gray
stroke(150);
rect(0, 0, 40, 80);
// then rotate
rotate(radians(65));
// draw again black
stroke(0);
rect(0, 0, 40, 80);
popMatrix();
// here using the cordinates calculated before as a translate value
// we draw at origin, but it appears at same place
stroke(230,230,100);
// a bit to the left...
translate(xpos-2, ypos-2);
rect(0, 0, 40, 80);
}
Rotation is aways applied to origin point (0,0), so you want to draw your axis for rotating at origin. Or move the coordinate system origin, to your axis. It is indeed confusing. When using transformations I tend to draw at origin always. Try this to see if it makes things more clear... or less :)
Also there is push/popMatrix() to control the scope of transformations...
public void setup() {
size(300, 380);
smooth();
}
public void draw() {
background(0);
stroke(255);
//use push/pop Matrix to control scope of translate and rotate
pushMatrix();
//move coordinates system origin to center of screen
translate(width/2, height/2);
// rotate with origin as axis
rotate(radians(mouseY));
//draw at origin (now temp moved to center of screen)
// white line
line(0, 0, 150, 0);
// here all coordinate system is back to normal
popMatrix();
//draw with normal system red line at origin
stroke(255, 0, 0);
line(0, 0, 150, 0);
//draw with normal system blue line at centre
stroke(0, 0, 255);
line(width/2, height/2, width/2 + 150, height/2);
}

Resources