Using pushMatrix(), popMatrix(), translate(), and rotate() - rotation

I have a program that draws a flower with 4 petals to the screen where ever you click. Instead of redoing the petals one at a time I was just going to attempt to rotate the flower by a quarter turn and have it overlay the first flower, so it appears to have 8 petals. The problem is that it draws the second flower some distance away from the first in the x and y direction, but as I get closer to (0,0) they get closer to overlapping (and do at (0, 0)). I'm not sure why this is happening and would appreciate any help. I'm using Processing 3 for this program.
int c_center = 15;
int c_petal = 20;
int petalsize = 70;
color rcol;
void setup(){
fullScreen();
background(0);
}
void draw(){
rcol = color(random(255), random(255), random(255));
}
void mouseClicked(){
pushMatrix();
flower();
translate(mouseX, mouseY);
rotate(PI/4);
flower();
popMatrix();
}
void flower(){
//left petal
make_petal(mouseX - (petalsize - c_petal), mouseY, petalsize, c_petal);
//right petal
make_petal(mouseX + (petalsize - c_petal), mouseY, petalsize, c_petal);
//top petal
make_petal(mouseX, mouseY - (petalsize - c_petal), c_petal, petalsize);
//bottom petal
make_petal(mouseX, mouseY + (petalsize - c_petal), c_petal, petalsize);
//flower center
fill(random(255), random(255), random(255));
ellipse(mouseX, mouseY, c_center, c_center);
}
void make_petal(int a, int b, int c, int d){
fill(rcol);
ellipse(a, b, c, d);
}

You're mixing two styles of drawing together: your flower() function uses mouseX and mouseY to determine where to draw the petals, but then you're also calling translate(mouseX, mouseY);. So you're drawing petals relative to the mouse, but then moving the entire canvas by the mouse position, and then drawing more petals relative to the mouse again.
Instead, you probably want to only use one style of drawing. Specifically, you probably don't want your flower() function to care about the mouse position. Treat it like a "stamp" that draws things around the origin at 0,0. Then move the origin by calling translate() and rotate the stamp by calling rotate().

I corrected the code thanks to feedback, so it works now in case anyone is interested. I condensed it a little by just making one petal then rotating it through a loop.
int c_center = 20;
int c_petal = int(random(5, 15));
int petalsize = int(random(30, 60));
color rcol;
void setup(){
fullScreen();
background(0);
}
void draw(){
rcol = color(random(255), random(255), random(255));
}
void mouseClicked(){
pushMatrix();
translate(mouseX, mouseY);
for(int r = 0; r < 360; r+=5){
rotate(radians(r));
flower();
}
popMatrix();
}
void flower(){
//petal
make_petal(0, 0 - (petalsize - c_petal), c_petal, petalsize);
//flower center
fill(random(255), random(255), random(255));
ellipse(0, 0, c_center, c_center);
}
void make_petal(int a, int b, int c, int d){
fill(rcol);
ellipse(a, b, c, d);
}

Related

How can add interaction and animation to shapes drawn in Processing?

I'm trying to code a canvas full of shapes(houses) and animate them in processing.
Here's an example of shape:
void house(int x, int y) {
pushMatrix();
translate(x, y);
fill(0, 200, 0);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
By animation I mean moving them in random directions.
I would also like to add basic interaction: when hovering over a house it's colour would change.
At the moment I've managed to render a canvas full of houses:
void setup() {
size(500, 500);
background(#74F5E9);
for (int i = 30; i < 500; i = i + 100) {
for (int j = 30; j < 500; j = j + 100) {
house(i, j);
}
}
}
void house(int x, int y) {
pushMatrix();
translate(x, y);
fill(0, 200, 0);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
Without seeing source code: your attempted sketch it's very hard to tell.
They can be animated in many ways and it's unclear what you mean. For example, is that the position/rotation/scale of each square, is it the corners/vertices of each square, both ?
You might have a clear idea in your mind, but the current form of the question is ambiguous. We also don't know you're comfort level with various notions such as classes/objects/PVector/PShape/etc. If you were to 'story board' this animation what would it look like ? Breaking the problem down and explaining it in a way that anyone can understand might actually help you figure out a solution on your own as well.
Processing has plenty of examples. Here are a few I find relevant based on what my understanding is of your problem.
You can have a look at the Objects and Create Shapes examples:
File > Examples > Basics > Objects > Objects: Demonstrates grouping drawing/animation (easing, damping). You can tweak this example draw a single square and once you're happy with the look/motion you can animate multiple using an array or ArrayList
File > Examples > Topics > Create Shapes > PolygonPShapeOOP3: Great example using PShape to animate objects.
File > Examples > Topics > Create Shapes > WigglePShape: This example demonstrates how to access and modify the vertices of a PShape
For reference I'm simply copy/pasting the examples mentioned above here as well:
Objects
/**
* Objects
* by hbarragan.
*
* Move the cursor across the image to change the speed and positions
* of the geometry. The class MRect defines a group of lines.
*/
MRect r1, r2, r3, r4;
void setup()
{
size(640, 360);
fill(255, 204);
noStroke();
r1 = new MRect(1, 134.0, 0.532, 0.1*height, 10.0, 60.0);
r2 = new MRect(2, 44.0, 0.166, 0.3*height, 5.0, 50.0);
r3 = new MRect(2, 58.0, 0.332, 0.4*height, 10.0, 35.0);
r4 = new MRect(1, 120.0, 0.0498, 0.9*height, 15.0, 60.0);
}
void draw()
{
background(0);
r1.display();
r2.display();
r3.display();
r4.display();
r1.move(mouseX-(width/2), mouseY+(height*0.1), 30);
r2.move((mouseX+(width*0.05))%width, mouseY+(height*0.025), 20);
r3.move(mouseX/4, mouseY-(height*0.025), 40);
r4.move(mouseX-(width/2), (height-mouseY), 50);
}
class MRect
{
int w; // single bar width
float xpos; // rect xposition
float h; // rect height
float ypos ; // rect yposition
float d; // single bar distance
float t; // number of bars
MRect(int iw, float ixp, float ih, float iyp, float id, float it) {
w = iw;
xpos = ixp;
h = ih;
ypos = iyp;
d = id;
t = it;
}
void move (float posX, float posY, float damping) {
float dif = ypos - posY;
if (abs(dif) > 1) {
ypos -= dif/damping;
}
dif = xpos - posX;
if (abs(dif) > 1) {
xpos -= dif/damping;
}
}
void display() {
for (int i=0; i<t; i++) {
rect(xpos+(i*(d+w)), ypos, w, height*h);
}
}
}
PolygonPShapeOOP3:
/**
* PolygonPShapeOOP.
*
* Wrapping a PShape inside a custom class
* and demonstrating how we can have a multiple objects each
* using the same PShape.
*/
// A list of objects
ArrayList<Polygon> polygons;
// Three possible shapes
PShape[] shapes = new PShape[3];
void setup() {
size(640, 360, P2D);
shapes[0] = createShape(ELLIPSE,0,0,100,100);
shapes[0].setFill(color(255, 127));
shapes[0].setStroke(false);
shapes[1] = createShape(RECT,0,0,100,100);
shapes[1].setFill(color(255, 127));
shapes[1].setStroke(false);
shapes[2] = createShape();
shapes[2].beginShape();
shapes[2].fill(0, 127);
shapes[2].noStroke();
shapes[2].vertex(0, -50);
shapes[2].vertex(14, -20);
shapes[2].vertex(47, -15);
shapes[2].vertex(23, 7);
shapes[2].vertex(29, 40);
shapes[2].vertex(0, 25);
shapes[2].vertex(-29, 40);
shapes[2].vertex(-23, 7);
shapes[2].vertex(-47, -15);
shapes[2].vertex(-14, -20);
shapes[2].endShape(CLOSE);
// Make an ArrayList
polygons = new ArrayList<Polygon>();
for (int i = 0; i < 25; i++) {
int selection = int(random(shapes.length)); // Pick a random index
Polygon p = new Polygon(shapes[selection]); // Use corresponding PShape to create Polygon
polygons.add(p);
}
}
void draw() {
background(102);
// Display and move them all
for (Polygon poly : polygons) {
poly.display();
poly.move();
}
}
// A class to describe a Polygon (with a PShape)
class Polygon {
// The PShape object
PShape s;
// The location where we will draw the shape
float x, y;
// Variable for simple motion
float speed;
Polygon(PShape s_) {
x = random(width);
y = random(-500, -100);
s = s_;
speed = random(2, 6);
}
// Simple motion
void move() {
y+=speed;
if (y > height+100) {
y = -100;
}
}
// Draw the object
void display() {
pushMatrix();
translate(x, y);
shape(s);
popMatrix();
}
}
WigglePShape:
/**
* WigglePShape.
*
* How to move the individual vertices of a PShape
*/
// A "Wiggler" object
Wiggler w;
void setup() {
size(640, 360, P2D);
w = new Wiggler();
}
void draw() {
background(255);
w.display();
w.wiggle();
}
// An object that wraps the PShape
class Wiggler {
// The PShape to be "wiggled"
PShape s;
// Its location
float x, y;
// For 2D Perlin noise
float yoff = 0;
// We are using an ArrayList to keep a duplicate copy
// of vertices original locations.
ArrayList<PVector> original;
Wiggler() {
x = width/2;
y = height/2;
// The "original" locations of the vertices make up a circle
original = new ArrayList<PVector>();
for (float a = 0; a < radians(370); a += 0.2) {
PVector v = PVector.fromAngle(a);
v.mult(100);
original.add(new PVector());
original.add(v);
}
// Now make the PShape with those vertices
s = createShape();
s.beginShape(TRIANGLE_STRIP);
s.fill(80, 139, 255);
s.noStroke();
for (PVector v : original) {
s.vertex(v.x, v.y);
}
s.endShape(CLOSE);
}
void wiggle() {
float xoff = 0;
// Apply an offset to each vertex
for (int i = 1; i < s.getVertexCount(); i++) {
// Calculate a new vertex location based on noise around "original" location
PVector pos = original.get(i);
float a = TWO_PI*noise(xoff,yoff);
PVector r = PVector.fromAngle(a);
r.mult(4);
r.add(pos);
// Set the location of each vertex to the new one
s.setVertex(i, r.x, r.y);
// increment perlin noise x value
xoff+= 0.5;
}
// Increment perlin noise y value
yoff += 0.02;
}
void display() {
pushMatrix();
translate(x, y);
shape(s);
popMatrix();
}
}
Update
Based on your comments here's an version of your sketch modified so the color of the hovered house changes:
// store house bounding box dimensions for mouse hover check
int houseWidth = 30;
// 30 px rect height + 15 px triangle height
int houseHeight = 45;
void setup() {
size(500, 500);
}
void draw(){
background(#74F5E9);
for (int i = 30; i < 500; i = i + 100) {
for (int j = 30; j < 500; j = j + 100) {
// check if the cursor is (roughly) over a house
// and render with a different color
if(overHouse(i, j)){
house(i, j, color(0, 0, 200));
}else{
house(i, j, color(0, 200, 0));
}
}
}
}
void house(int x, int y, color fillColor) {
pushMatrix();
translate(x, y);
fill(fillColor);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
// from Processing RollOver example
// https://processing.org/examples/rollover.html
boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}
// check if the mouse is within the bounding box of a house
boolean overHouse(int x, int y){
// offset half the house width since the pivot is at the tip of the house
// the horizontal center
return overRect(x - (houseWidth / 2), y, houseWidth, houseHeight);
}
The code is commented, but here are the main takeaways:
the house() function has been changed so you can specify a color
the overRect() function has been copied from the Rollover example
the overHouse() function uses overRect(), but adds a horizontal offset to take into account the house is drawn from the middle top point (the house tip is the shape's pivot point)
Regarding animation, Processing has tons of examples:
https://processing.org/examples/sinewave.html
https://processing.org/examples/additivewave.html
https://processing.org/examples/noise1d.html
https://processing.org/examples/noisewave.html
https://processing.org/examples/arrayobjects.html
and well as the Motion / Simulate / Vectors sections:
Let's start take sine motion as an example.
The sin() function takes an angle (in radians by default) and returns a value between -1.0 and 1.0
Since you're already calculating positions for each house within a 2D grid, you can offset each position using sin() to animate it. The nice thing about it is cyclical: no matter what angle you provide you always get values between -1.0 and 1.0. This would save you the trouble of needing to store the current x, y positions of each house in arrays so you can increment them in a different directions.
Here's a modified version of the above sketch that uses sin() to animate:
// store house bounding box dimensions for mouse hover check
int houseWidth = 30;
// 30 px rect height + 15 px triangle height
int houseHeight = 45;
void setup() {
size(500, 500);
}
void draw(){
background(#74F5E9);
for (int i = 30; i < 500; i = i + 100) {
for (int j = 30; j < 500; j = j + 100) {
// how fast should each module move around a circle (angle increment)
// try changing i with j, adding i + j or trying other mathematical expressions
// also try changing 0.05 to other values
float phase = (i + frameCount) * 0.05;
// try changing amplitude to other values
float amplitude = 30.0;
// map the sin() result from it's range to a pixel range (-30px to 30px for example)
float xOffset = map(sin(phase), -1.0, 1.0, -amplitude, amplitude);
// offset each original grid horizontal position (i) by the mapped sin() result
float x = i + xOffset;
// check if the cursor is (roughly) over a house
// and render with a different color
if(overHouse(i, j)){
house(x, j, color(0, 0, 200));
}else{
house(x, j, color(0, 200, 0));
}
}
}
}
void house(float x, float y, color fillColor) {
pushMatrix();
translate(x, y);
fill(fillColor);
triangle(15, 0, 0, 15, 30, 15);
rect(0, 15, 30, 30);
rect(12, 30, 10, 15);
popMatrix();
}
// from Processing RollOver example
// https://processing.org/examples/rollover.html
boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}
// check if the mouse is within the bounding box of a house
boolean overHouse(int x, int y){
// offset half the house width since the pivot is at the tip of the house
// the horizontal center
return overRect(x - (houseWidth / 2), y, houseWidth, houseHeight);
}
Read through the comments and try to tweak the code to get a better understanding of how it works and have fun coming up with different animations.
The main changes are:
modifying the house() function to use float x,y positions (instead of int): this is to avoid converting float to int when using sin(), map() and get smoother motions (instead of motion that "snaps" to whole pixels)
Mapped sine to positions which can be used to animate
Wrapping the 3 instructions that calculate the x offset into a reusable function would allow you do further experiment. What if you used a similar technique the y position of each house ? What about both x and y ?
Go through the code step by step. Try to understand it, change it, break it, fix it and make new sketches reusing code.

Trying to get the Lives and Game Over portion of my game to work

I'm creating a collision detection game where:
Every time I hit the wall, the number of my lives go down.
Once I get to 0 lives, the game is over.
But the game is letting me go into negative lives. Also, my click to begin once you win doesn't seem to work either... Does anybody know how to fix this?
PImage startScreen;
int gamestate=1;
int lives = 3;
class Sprite {
float x;
float y;
float dx;
float dy;
}
Sprite rect=new Sprite();
Sprite ball=new Sprite();
void setup(){
size(500,500);
rect.x = 500;
rect.y = 12;
ball.y= mouseY;
background(0);
fill(0,255,0);
text("Click to Begin", 10, 250);
}
void draw(){
if(gamestate ==0){
background(0);
fill(0, 255,0);
noStroke();
rect(0,235, 500,2.5);
rect(0,250, 500,2.5);
fill(0);
rect(0,238,rect.x,rect.y);
fill(0,255,0);
ellipse(mouseX, mouseY, 2,2);
text("lives left:"+lives, 10, 20);
if (mouseY<240 || mouseY>247){
background(0);
lives = lives-1;
if(lives <= 0){
text("Game Over. \nClick to Begin", 225,250);
gamestate=1;
}
}
if (mouseX >= 495){
background(0);
text("You Win! \nClick to Begin Again.", 225,250);
}
}
}
void mousePressed(){
if (gamestate ==1){
gamestate=0;
}
}
Keep in mind that before your mouse enters the sketch for the first time, mouseX and mouseY are both 0. So in your draw() function, when you check if mouseY < 240, that's true. You do that 60 times per second, so you lose all 3 of your lives right away.
To fix this, you might want to have a "starting rectangle" that the player has to click to start the game, that way you know the mouse is in the window.
Then after that, you have to give the player a chance to get back to the starting circle before starting the next round, otherwise you just keep losing lives 60 times per second.
That basics might look something like this:
int gamestate=1; //1 is start screen, 0 is playing, 2 is between lives, 3 wins
int lives = 3;
class Sprite {
float x;
float y;
float dx;
float dy;
}
Sprite rect=new Sprite();
Sprite ball=new Sprite();
void setup() {
size(500, 500);
rect.x = 500;
rect.y = 12;
ball.y= mouseY;
}
void draw() {
background(0);
if (gamestate ==0) {
fill(0, 255, 0);
noStroke();
rect(0, 235, 500, 2.5);
rect(0, 250, 500, 2.5);
fill(0);
rect(0, 238, rect.x, rect.y);
fill(0, 255, 0);
ellipse(mouseX, mouseY, 2, 2);
text("lives left:"+lives, 10, 20);
if (mouseY<240 || mouseY>247) {
lives = lives-1;
gamestate=2;
if (lives <= 0) {
text("Game Over. \nClick to Begin", 225, 250);
gamestate=1;
}
}
if (mouseX >= 495) {
gamestate=3;
}
}
else if(gamestate == 1 || gamestate==2){
fill(0, 255, 0);
text("Click to Begin", 10, 230);
rect(0, 240, width, 7);
}
else if(gamestate == 3){
text("You Win! \nClick to Begin Again.", 225, 250);
}
}
void mousePressed() {
if (gamestate ==1 || gamestate == 2) {
if(mouseY>240 && mouseY<247){
gamestate=0;
}
}
}
Note that I don't really understand what your game is supposed to do: your "safe area" is only 7 pixels tall, which seems pretty small. But assuming this is just an example, my answer should generalize to your real code:
Split your game up into "modes". You started to do this with your gamestate variable, but you're also mixing event code and drawing code. Instead, make every mode a state: the start screen, the playing screen, the "in between lives" screen, the game over screen. Only draw the stuff for that mode, and then change the mode based on input. Basically, instead of checking for "play mode" and then drawing "game over" when you run out of lives, just switch to "game over mode" and let that part of the code draw the "game over" to the screen.

Stop background from refreshing?

I am trying to get my gif to do something similar to this gif.
I have been able to get the line to draw, and the 'planets' to orbit, but can't figure out how to keep the line connecting the two circles, like the gif does.
Here's the basic code:
int x = 500;
int y = 500;
int radius = y/2;
int cX = x/2;
int cY = y/2;
String text1;
int lg_xBall;
int lg_yBall;
int sm_xBall;
int sm_yBall;
void setup() {
size(x, y);
smooth();
colorMode(RGB);
}
void draw() {
background(0);
stroke(255);
float t = millis()/1000.0f;
drawSmBallOrbit(100);
drawLgBallOrbit(100);
moveSmBall(t);
moveLgBall(t);
sun();
// showMouse();
connectingLines();
}
void drawCircle() { // This will draw a simple circle
stroke(1);
// x1=a+r*cos t, y1=b+r*sin t
ellipse(x/2, y/2, x/2, y/2);
}
void drawLines() { // This will draw lines from the center of the circle.
stroke(1);
line(x/2, y/2, radius/2, radius); // line from 6 to center
line(x/2, y/2, x/2, y/4); // line from 12 to center
for (int i = 0; i <= 5; i+=2.5) {
float x1 = x/2+radius/2*cos(i);
float y1 = y/2+radius/2*sin(i);
line(x/2, y/2, x1, y1);
}
}
void moveSmBall(float ky) { // This will create, and move, a small 'planet'
pushStyle();
stroke(100);
sm_xBall = (int)(cX+radius*cos(ky));
sm_yBall = (int)(cY+radius*sin(ky));
fill(190, 0, 0);
// background(0);
ellipse(sm_xBall, sm_yBall, 10, 10);
popStyle();
}
void drawSmBallOrbit(float opacity) {
pushStyle();
stroke(255, opacity);
strokeWeight(1);
noFill();
ellipse(x/2, y/2, cX+radius, cY+radius);
popStyle();
}
void moveLgBall(float kx) {
kx = kx/.7;
pushStyle();
lg_xBall = (int)(cX+radius*cos(kx)*.6);
lg_yBall = (int)(cY+radius*sin(kx)*.6);
fill(0, 0, 230);
ellipse(lg_xBall, lg_yBall, 30, 30);
popStyle();
}
void drawLgBallOrbit(float opacity) {
pushStyle();
stroke(255, opacity);
strokeWeight(1);
noFill();
ellipse(x/2, y/2, (cX+radius)*.6, (cY+radius)*.6);
popStyle();
}
void sun() {
pushStyle();
fill(250, 250, 0);
ellipse(cX, cY, 40, 40);
popStyle();
}
void connectingLines() {
line(sm_xBall, sm_yBall, lg_xBall, lg_yBall);
}
void showMouse() {
text("X: " + mouseX, x/2, y/2-30);
text("Y: " + mouseY, x/2, y/2-50);
}
Thanks for any help/advice!
The problem is that you're calling background() during every frame, which will clear away anything you've already drawn.
So you either need to stop calling background(), or you need to redraw the old lines every frame.
If you simply move the call to background() out of your draw() function and into your setup() function, you're about 50% there already:
void setup() {
size(x, y);
smooth();
colorMode(RGB);
background(0);
}
void draw() {
// background(0);
stroke(255);
float t = millis()/1000.0f;
drawSmBallOrbit(100);
drawLgBallOrbit(100);
moveSmBall(t);
moveLgBall(t);
sun();
// showMouse();
connectingLines();
}
However, the original animation does not show the previous positions of the ellipses. So you need to clear away the previous frame by calling the background() function, and then redraw previous line positions. You'd do that by having an ArrayList that holds those previous positions.
Here's a simple example that uses an ArrayList to redraw anywhere the mouse has been:
ArrayList<PVector> points = new ArrayList<PVector>();
void setup() {
size(500, 500);
}
void draw() {
background(0);
stroke(255);
points.add(new PVector(mouseX, mouseY));
for(PVector p : points){
ellipse(p.x, p.y, 10, 10);
}
}
You would need to do something very similar, but you'd have to keep track of two points at a time instead of one, since you're tracking two ellipses and not just the mouse position.

How to rotate a line in a circle (radar like) in Processing while also plotting points?

I am trying to rotate a line around in a circle that represents the direction a sensor is facing, while also plotting distance measurements. So I can't use background() in the draw function to clear the screen, because it erases the plotting of the distance readings. I've tried pggraphics and a few others ways, but can't seem to find a way to do it.
This is what I have right now:
void setup() {
background(255,255,255);
size(540, 540);
}
void draw() {
translate(width/2, height/2);
ellipse(0,0,100,100);
newX = x*cos(theta)- y*sin(theta);
newY = x*sin(theta)+ y*cos(theta);
theta = theta + PI/100;
//pushMatrix();
fill(255, 255);
line(0, 0, newX, newY);
rotate(theta);
//popMatrix();
}
I am new to Processing, and coding in general, but can anyone point me in the right direction on how to do this? Thanks
This is what it outputs: http://imgur.com/I825mjE
You can use background(). You just need to redraw the readings on each frame. You could store the readings in an ArrayList, which allows you to add new readings, change them and remove them.
An example:
ArrayList<PVector> readings;
int readingsCount = 15;
void setup() {
size(540, 540);
// create collection of random readings
readings = new ArrayList<PVector>();
for(float angle = 0; angle < TWO_PI; angle += TWO_PI/ readingsCount) {
float distance = random(100, 200);
// the new reading has an angle...
PVector newReading = PVector.fromAngle(angle);
// ... and a distance
newReading.mult(distance);
// Add the new reading to the collection
readings.add(newReading);
}
}
void draw() {
background(255);
// Put (0, 0) in the middle of the screen
translate(width/2, height/2);
float radius = 250;
noFill();
ellipse(0, 0, 2*radius, 2*radius);
// draw spinning line
float angle = frameCount * 0.1;
line(0, 0, radius * cos(angle), radius * sin(angle));
// draw readings
for(PVector p : readings) {
ellipse(p.x, p.y, 20, 20);
}
}

How to animate a 3D curve between two points on a map?

I am trying to do a twitter visualization.
I using curves to connect two points on the map.
Here is the code which I am using for it. It has been taken from an example by Chrisir from the processing forums.
void setup()
{
size( 800, 800, P3D );
} // setup
void draw()
{
// myCurveTest() ;
PVector firstpoint = new PVector (120, 320, -30);
PVector secondpoint = new PVector (320, 220, -30);
myCurve (firstpoint, secondpoint ) ;
firstpoint = new PVector (420, 220, 30);
secondpoint = new PVector (620, 120, -30);
myCurve (firstpoint, secondpoint ) ;
}
void myCurve (
PVector firstpoint,
PVector secondpoint) {
PVector beginningcontrolpoint = new PVector (120, firstpoint.y+1200, -30);
PVector endingcontrolpoint = new PVector (720, secondpoint.y+1200, -30);
myPointPVector(beginningcontrolpoint, color(255, 0, 0));
myPointPVector(firstpoint, color(0, 0, 255));
myPointPVector(secondpoint, color(0, 0, 255));
myPointPVector(endingcontrolpoint, color(255, 0, 0));
stroke (255);
noFill();
curve(
beginningcontrolpoint.x, beginningcontrolpoint.y, beginningcontrolpoint.z,
firstpoint.x, firstpoint.y, firstpoint.z,
secondpoint.x, secondpoint.y, secondpoint.z,
endingcontrolpoint.x, endingcontrolpoint.y, endingcontrolpoint.z);
}
void myPointPVector (PVector test, color col1) {
MyBox(test.x, test.y, test.z,
test.x+3, test.y, test.z,
9,
col1);
}
void MyBox(float x1, float y1, float z1, float x2, float y2, float z2, float weight, color strokeColour)
// was called drawLine; programmed by James Carruthers
// see http://processing.org/discourse/yabb2/YaBB.pl?num=1262458611/0#9
{
PVector p1 = new PVector(x1, y1, z1);
PVector p2 = new PVector(x2, y2, z2);
PVector v1 = new PVector(x2-x1, y2-y1, z2-z1);
float rho = sqrt(pow(v1.x, 2)+pow(v1.y, 2)+pow(v1.z, 2));
float phi = acos(v1.z/rho);
float the = atan2(v1.y, v1.x);
v1.mult(0.5);
pushMatrix();
translate(x1, y1, z1);
translate(v1.x, v1.y, v1.z);
rotateZ(the);
rotateY(phi);
noStroke();
fill(strokeColour);
box(weight, weight, p1.dist(p2)*1.2);
popMatrix();
}
I want to animated this 3D curve so that I can see them being drawn on the map. Can anyone help me out with this. I have tried everything from framecount to advanced animation libraried in processing but no luck yet :(
Thanks.
You may just calculate a parabola point by point, draw it using curveVertex and orbit it in 3D using translate and rotate, here an example (using 1.5.1/P3D/fontMode(SCREEN)):
float initial_x = -200;
float x = initial_x;
float y;
float y_offset;
float r = 200;// change this to change the height of the parabola
ArrayList<PVector> pts = new ArrayList<PVector>();
float mx = 70, my = -15, tmx, tmy;
boolean animating = false;
PFont f;
void setup() {
size( 800, 400, P3D);
background(255);
smooth();
f = createFont("Arial", 15);
textMode(SCREEN);
}
void draw() {
//lights();
background(255);
fill(100);
textFont(f, 15);
text("drag to orbit", width - 10 - textWidth("drag to orbit"), height -30);
text("any key to redraw parabola", width - 10 - textWidth("any key to redraw parabola"), height -10);
//rotate 3d
translate(width/4, height/2);
rotateY(radians(mx));
rotateZ(radians(my));
// to mark origin and help view 3d
noFill();
stroke(100);
box(20);
pushMatrix();
translate(100, 5, -100);
stroke(200);
fill(0, 20);
box(600, 2, 600);
popMatrix();
//store y offset
if (x == initial_x) {
y_offset = (sq(x)+2*x)/r;
}
// stop parabola
if ( x == initial_x || x < -initial_x + 2) {
x+=2;
animating = true;
// add to curve storage
pts.add(new PVector(x, y));
}
else {
animating = false;
}
//calc y
y = (sq(x)+2*x)/r - y_offset;
stroke(50, 30, 7);
noFill();
strokeWeight(1);
//draw at origin
translate(-initial_x, 0);
//draw curve
beginShape();
for (PVector p:pts) {
curveVertex(p.x, p.y);
}
endShape();
//draw a ball
if (!animating) {
translate(pts.get(frameCount%pts.size()).x, pts.get(frameCount%pts.size()).y);
noStroke();
fill(220, 190, 35);
sphere(4);
}
}
void mousePressed() {
tmx = mouseX;
tmy = mouseY;
}
void mouseDragged() {
mx = tmx - mouseX;
my = tmy - mouseY;
}
void keyPressed() {
x = -200;
pts.clear();
}
You're drawing the curves using the curve() command (http://processing.org/reference/curve_.html) which draws a Catmull-Rom spline. In your code, you're only drawing a single spline section (the one between the two control points), so what you're really interested in is "how can I draw only part of a Catmull-Rom spline section". I don't have the answer for that one, but if you change your curve(control1, first, second, control2) call into a bezier(first,c1,c2,second) call (where you will now have to come up with the code for the control points c1 and c2) instead, then you can use de Casteljau's algorithm to cut up a curve into two segments anywhere along it. If you slide up the where-to-cut-it point every frame, and then only draw the first segment that you get from the split operation, it'll look like a curve that draws itself from the start to the end point. See http://pomax.github.io/bezierinfo/#splitting for the description on how to do this (bonus: the source code is even in Processing)
Make use of the curvePoint() method. Here is a resolution to a similar problem:
http://forum.processing.org/one/topic/animation-with-curve.html.

Resources