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.
Related
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!
I have a problem and although I serached everywhere I couldn't find a solution.
I have a stacked sprite and I'm rotating this sprite around the center of the screen. So I iterate over a list of sprites (stacked) and increase the y-coordinate by 2 every loop (rotation is increased step by step by 0.01f outside of the loop):
foreach(var s in stacked)
{
Vector2 origin = new Vector2(Basic.width / 2, Basic.height / 2);
Rectangle newPosition = new Rectangle(position.X, position.Y - y, position.Width, position.Height);
float angle = 0f;
Matrix transform = Matrix.CreateTranslation(-origin.X, -origin.Y, 0f) *
Matrix.CreateRotationZ(rotation) *
Matrix.CreateTranslation(origin.X, origin.Y, 0f);
Vector2 pos = new Vector2(newPosition.X, newPosition.Y);
pos = Vector2.Transform(pos, transform);
newPosition.X = (int)pos.X;
newPosition.Y = (int)pos.Y;
angle += rotation;
s.Draw(newPosition, origin, angle, Color.White);
y += 2;
}
This works fine. But now my problem. I want not only to rotate the sprite around the center of the screen but also around itself. How to achieve this? I can only set one origin and one rotation per Draw. I would like to rotate the sprite around the origin 'Basic.width / 2, Basic.height / 2' and while it rotates, around 'position.Width / 2, position.Height / 2'. With different rotation speed each. How is this possible?
Thank you in advance!
Just to be clear:
When using SpriteBatch.Draw() with origin and angle, there is only one rotation: the final angle of the sprite.
The other rotations are positional offsets.
The origin in the Draw() call is a translation, rotation, translate back. Your transform matrix shows this quite well:
Matrix transform = Matrix.CreateTranslation(-origin.X, -origin.Y, 0f) *
Matrix.CreateRotationZ(rotation) *
Matrix.CreateTranslation(origin.X, origin.Y, 0f);
//Class level variables:
float ScreenRotation, ScreenRotationSpeed;
float ObjectRotation, ObjectRotationSpeed;
Vector2 ScreenOrigin, SpriteOrigin;
// ...
// In constructor and resize events:
ScreenOrigin = new Vector2(Basic.width <<1, Basic.height <<1);
// shifts are faster for `int` type. If "Basic.width" is `float`:
//ScreenOrigin = new Vector2(Basic.width, Basic.height) * 0.5f;
// In Update():
ScreenRotation += ScreenRotationSpeed; // * gameTime.ElapsedGameTime.Seconds; // for FPS invariant speed where speed = 60 * single frame speed
ObjectRotation+= ObjectRotationSpeed;
//Calculate the screen center rotation once per step
Matrix baseTransform = Matrix.CreateTranslation(-ScreenOrigin.X, -ScreenOrigin.Y, 0f) *
Matrix.CreateRotationZ(ScreenRotation) *
Matrix.CreateTranslation(ScreenOrigin.X, ScreenOrigin.Y, 0f);
// In Draw() at the start of your code snippet posted:
// moved outside of the loop for a translationally invariant vertical y interpretation
// or move it inside the loop and apply -y to position.Y for an elliptical effect
Vector2 ObjectOrigin = new Vector2(position.X, position.Y);
Matrix transform = baseTransform *
Matrix.CreateTranslation(-ObjectOrigin.X, -ObjectOrigin.Y, 0f) *
Matrix.CreateRotationZ(ObjectRotation) *
Matrix.CreateTranslation(ObjectOrigin.X, ObjectOrigin.Y, 0f);
foreach(var s in stacked)
{
Vector2 pos = new Vector2(ObjectOrigin.X, ObjectOrigin.Y - y);
pos = Vector2.Transform(pos, transform);
float DrawAngle = ObjectRotation;
// or float DrawAngle = ScreenRotation;
// or float DrawAngle = ScreenRotation + ObjectRotation;
// or float DrawAngle = 0;
s.Draw(pos, SpriteOrigin, DrawAngle, Color.White);
}
I suggest moving the Draw() parameter away from destinationRectangle and use the Vector2 position directly with scaling. Rotations within square rectangles can differ up to SQRT(2) in aspect ratio, i.e. stretching/squashing. Using Vector2 incurs a cost of higher collision complexity.
I am sorry for the ors, but without complete knowledge of the problem...YMMV
In my 2D projects, I use the vector form of polar coordinates.
The Matrix class requires more calculations than the polar equivalents in 2D. Matrix operates in 3D, wasting cycles calculating Z components.
With normalized direction vectors (cos t,sin t) and a radius(vector length),in many cases I use Vector2.LengthSquared() to avoid the square root when possible.
The only time I have used Matrices in 2D is display projection matrix(entire SpriteBatch) and Mouse and TouchScreen input deprojection(times the inverse of the projection matrix)
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();
class Particle{
PVector velocity, location; //PVector variables for each particle.
Particle(){ //Constructor - random location and speed for each particle.
velocity = new PVector(random(-0.5,0.5), random(-0.5,0.5));
location = new PVector(random(0,width),random(0,width));
}
void update() { location.add(velocity); } //Motion method.
void edge() { //Wraparound case for particles.
if (location.x > width) {location.x = 0;}
else if (location.x < 0) {location.x = width;}
if (location.y > height) {location.y = 0;}
else if (location.y < 0) {location.y = height;}
}
void display(ArrayList<Particle> p){ //Display method to show lines and ellipses between particles.
for(Particle other: p){ //For every particle in the ArrayList.
float d = PVector.dist(location,other.location); //Get distance between any two particle.
float a = 255 - d*2.5; //Map variable 'a' as alpha based on distance. E.g. if distance is high, d = 100, alpha is low, a = 255 - 225 = 30.
println("Lowest distance of any two particle =" + d); //Debug output.
if(d<112){ //If the distance of any two particle falls bellow 112.
noStroke(); //No outline.
fill(0,a); //Particle are coloured black, 'a' to vary alpha.
ellipse(location.x, location.y, 8, 8); //Draw ellipse based on location of particle.
stroke(0,a); //Lines are coloured black, 'a' to vary alpha.
strokeWeight(0.7);
line(location.x,location.y,other.location.x,other.location.y); //Draw line between four coordinates, between two particle.
}
}
}
}
ArrayList<Particle> particles = new ArrayList<Particle>(); //Create a new arraylist of type Particle.
void setup(){
size(640,640,P2D); //Setup frame of sketch.
particles.add(new Particle()); //Add five Particle elements into arraylist.
particles.add(new Particle());
particles.add(new Particle());
particles.add(new Particle());
particles.add(new Particle());
}
void draw(){
background(255); //Set white background.
for(Particle p: particles){ //For every 'p' of type Particle in arraylist particles.
p.update(); //Update location based on velocity.
p.display(particles); //Display each particle in relation to other particles.
p.edge(); //Wraparound if particle reaches edge of screen.
}
}
In the above code, there are to shape objects, lines and ellipses. The transparency of which are affected by variable a.
Variable 'a', or alpha, is extrapolated from 'd' which is distance. Hence, when the objects are further, the alpha value of the objects falls.
In this scenario, the alpha values of the line do not change over time e.g. fade with distance. However the ellipses seem to be stuck on alpha '255' despite having very similar code.
If the value of 'a' is hardcoded, e.g.
if(d<112){ //If the distance of any two particle falls bellow 112.
noStroke(); //No outline.
fill(0,100); //Particle are coloured black, set alpha 'a' to be 100, grey tint.
ellipse(location.x, location.y, 8, 8); //Draw ellipse based on location of particle.
the ellipses changes colour as expected to a grey tint.
Edit: I believe I have found the root of the issue. The variable 'a' does not discriminate between the particles that are being iterated. As such, the alpha might be stuck/adding up to 255.
You're going to have to post an MCVE. Note that this should not be your entire sketch, just a few hard-coded lines so we're all working from the same code. We should be able to copy and paste your code into our own machines to see the problem. Also, please try to properly format your code. Your lack of indentation makes your code hard to read.
That being said, I can try to help in a general sense. First of all, you're printing out the value of a, but you haven't told us what its value is. Is its value what you expect? If so, are you clearing out previous frames before drawing the ellipses, or are you drawing them on top of previously drawn ellipses? Are you drawing ellipses elsewhere in your code?
Start over with a blank sketch, and add just enough lines to show the problem. Here's an example MCVE that you can work from:
stroke(0);
fill(0);
ellipse(25, 25, 25, 25);
line(0, 25, width, 25);
stroke(0, 128);
fill(0, 128);
ellipse(75, 75, 25, 25);
line(0, 75, width, 75);
This code draws a black line and ellipse, then draws a transparent line and ellipse. Please hardcode the a value from your code, or add just enough code so we can see exactly what's going on.
Edit: Thanks for the MCVE. Your updated code still has problems. I don't understand this loop:
for(Particle other: p){ //For every particle in the ArrayList.
float d = PVector.dist(location,other.location); //Get distance between any two particle.
float a = 255 - d*2.5; //Map variable 'a' as alpha based on distance. E.g. if distance is high, d = 100, alpha is low, a = 255 - 225 = 30.
println("Lowest distance of any two particle =" + d); //Debug output.
if(d<112){ //If the distance of any two particle falls bellow 112.
noStroke(); //No outline.
fill(0,a); //Particle are coloured black, 'a' to vary alpha.
ellipse(location.x, location.y, 8, 8); //Draw ellipse based on location of particle.
stroke(0,a); //Lines are coloured black, 'a' to vary alpha.
strokeWeight(0.7);
line(location.x,location.y,other.location.x,other.location.y); //Draw line between four coordinates, between two particle.
}
}
}
You're saying for each Particle, you loop through every Particle and then draw an ellipse at the current Particle's location? That doesn't make any sense. If you have 100 Particles, that means each Particle will be drawn 100 times!
If you want each Particle's color to be based off its distance to the closest other Particle, then you need to modify this loop to simply find the closest Particle, and then base your calculations off of that. It might look something like this:
Particle closestNeighbor = null;
float closestDistance = 100000;
for (Particle other : p) { //For every particle in the ArrayList.
if (other == this) {
continue;
}
float d = PVector.dist(location, other.location);
if (d < closestDistance) {
closestDistance = d;
closestNeighbor = other;
}
}
Notice the if (other == this) { section. This is important, because otherwise you'll be comparing each Particle to itself, and the distance will be zero!
Once you have the closestNeighbor and the closestDistance, you can do your calculations.
Note that you're only drawing particles when they have a neighbor that's closer than 112 pixels away. Is that what you want to be doing?
If you have a follow-up question, please post an updated MCVE in a new question. Constantly editing the question and answer gets confusing, so just ask a new question if you get stuck again.
I am trying to rotate a rectangle without using the rotate function, but instead using a matrix..I know how to rotate a line using a matrix but all my attempts to rotate a rectangle have failed.
I dont think this is use full but heres is my code that rotates the line.
float[][] rotation;
float[] position;
float theta = 180;
float pointX;
float pointY;
void setup() {
frameRate(60);
size(600, 600);
pointX = 0;
pointY = 0;
rotation = new float[2][2];
position = new float[8];
}
void draw() {
background(200);
theta = mouseX;
position[0] = mouseY;
position[1] = mouseY;
position[2] = -mouseY;
position[3] = mouseY;
rotation[0][0] = cos(radians(theta));
rotation[0][1] = -sin(radians(theta));
rotation[1][0] = sin(radians(theta));
rotation[1][1] = cos(radians(theta));
float newpos[] = new float[8];
newpos[0] += position[0] * rotation[0][0];
newpos[1] += position[1] * rotation[0][1];
translate(width/2, height/2);
line(0, 0, pointX+newpos[0], pointY+newpos[1]);
line(0, 0, pointX+newpos[0] * -1, pointY+newpos[1] * -1);
}
Although the lines behaves properly it is by chance... You have a crucial part of the calculation of the new x and y of the point not as it should have been. As you can find in wikipedia, you need to calculate the sin and cos in the matrix as you properly did, but when creating the new point you don't exactly do this:
Start by having a look at pushMatrix()/popMatrix() and coordinate spaces.
Have a look at Daniel Shiffman's tutorial as well, it's pretty well explained.
If you need to get lower level than this, have a look at the PMatrix2D class.
Notice there is a rotate() function. After you rotate, you can either simply apply
the matrix(using applyMatrix()) but you might as well be using push/pop matrix calls.
Another option is multiply vectors(rectangle corners) to the rotation matrix and draw the result/transformed points.