# Rotation and Easing

Hello

In my sketch I want to controle the rotation of a wheel and add a bit of easing to the rotation to give some feeling of weight to the wheel.
I have it doing what I want but, the easing is messed up by the gap between 0 and 360°

You will understand the problem very easily if you try the sketch

How would you solve this problem ?

here is the code

``````int cx, cy;
float rot = 0;

float hypo ;
float mySin ;
float myCos ;
float alpha;

float easing = 0.1;

void setup() {
size(700, 700);

cx = width / 2;
cy = height / 2;
}

void draw() {

background(128);
update();
render();
}

void update() {

hypo = dist(mouseX, mouseY, cx, cy);
mySin = (mouseY - cy) / hypo;
myCos = (mouseX - cx)/ hypo;

if (mySin > 0) {
alpha = acos(myCos);
} else {
alpha = (2*PI) - acos(myCos);
}

float targetRot = alpha;
float dRot = targetRot - rot;

rot += dRot * easing;
}

void render() {
pushMatrix();
translate(width/2, height/2);

rotate (rot);
fill(255);

fill(128);
ellipse(150, 0, 50, 50);

popMatrix();
}
``````
1 Like

I think this should help:

3 Likes

I tried to calculate the shortest way and it seems to work:

``````  if(rot - alpha > alpha - (rot - TWO_PI)) {
rot -= TWO_PI;
} else if(alpha - rot > rot - (alpha - TWO_PI)) {
rot += TWO_PI;
}

float targetRot = alpha;
float dRot = targetRot - rot;

rot += dRot * easing;
``````

The logic of my code is: if rotating the angle all the way +TWO_PI or -TWO_PI makes distance to get shorter, than do it.

3 Likes

@phoebus , i could not stop play with that excellent idea,
but i could not solve the problem, but thanks to
@brunoruchiga can now show this version:

``````// https://discourse.processing.org/t/rotation-and-easing/5704
PVector myMouse = new PVector();
float rot = 0, alpha, dalpha, easing = 0.03;
//__________________________________________________________
void setup() {
size(600, 600);
stroke(120,120,120);
}
//__________________________________________________________
void draw() {
myMouse.set( mouseX, mouseY, 0);
myMouse.sub(width/2, height/2, 0);
dalpha = (alpha-rot)/PI;
if      ( dalpha < -1)       rot -= TWO_PI;  // @brunoruchiga
else if ( dalpha > 1 )       rot += TWO_PI;
rot += (alpha -rot) * easing;                // @phoebus
// draw
background(0, 0, 50);
translate(width/2, height/2);
fill(170, 170, 0);
ellipse(0, 0, 400, 400);
rotate (rot);
fill(170, 170, 170);
ellipse(150, 0, 100, 100);
}

``````
4 Likes

EXCALTY !

thank you so much @brunoruchiga, you nailed it !

1 Like

Hi,

I have been using this excellent sketch to simulate the behaviour of some rotating objects in Cinema 4D having converted the sketch to Python.

I am wondering if there is a way to increase the easing from - let’s say - 0.1 to 0.5 and then back from 0.5 to 0.1

I am trying to more realistically simulate some sort of acceleration/deceleration curve. I think this would be a way of doing it.

Any other suggestions?

You could take the easing values from a array that you fill from sin function

Or use sin directly together with map

you can try using the mouseY
means if top go slow, if bottom, go strong.

``````rot += (alpha -rot) * constrain( easing *height/(height-mouseY),0.01,0.1);
``````

Hi @kll,

Thanks for your reply. This doesn’t seem to be generating a changing value between 0.01 and 0.1

no, but is the effect that what you want?

play more

``````  float easing_var =  easing *height/(height-mouseY);
print(easing_var);
easing_var = constrain(easing_var,0.01,0.3);
println(" limit "+easing_var);
rot += (alpha -rot) * easing_var;                // @phoebus

``````

but it feels to strong now

the constrain has 2 jobs,

• limit the effect ( but should not be needed with good math )
• limit against mouse leaves canvas
1 Like

I was tinkering with the original post to simulate angular acceleration\deceleration.
Just sharing…

It behaves very differently with rotation direction.

``````int cx, cy;
float rot = 0;

float hypo ;
float mySin ;
float myCos ;
float alpha;

float easing = .01f;

float mod;
float lastRot;

float loc = 0;
float vel = 0;
float acc = TAU/2000;
int i;

public void settings()
{
size(1000, 700);
}

public void setup()
{
cx = width / 2;
cy = height / 2;
background(0);
}

public void draw()
{
//background(0);
if (mousePressed)
{
update();
}
render();
}

public void update()
{
hypo = dist(mouseX, mouseY, cx, cy);
mySin = (mouseY - cy) / hypo;
myCos = (mouseX - cx)/ hypo;

if (mySin > 0) {
alpha = acos(myCos);
} else {
alpha = (2*PI) - acos(myCos);
}

float targetRot = alpha;
float dRot = targetRot - rot;
//rot += dRot * easing;

rot += (loc/20); // Change this value for "speed"
lastRot = rot;

float trot = dRot;
loc += vel; //angle
vel += acc; //angular acceleration
if (loc < 0)
{
vel = 0;
loc = 0;
}

if (loc >= trot)
{
//vel = -vel;
//loc = trot;
vel = 0;
loc = trot;
}

//println(rot, lastRot, rot-lastRot);
println(loc, vel, acc);
}

public void render()
{
pushMatrix();
translate(2*width/3, height/2);

strokeWeight(5);
stroke(0, 255, 0);
rotate (rot);
fill(128);

fill(0, 255, 0);
ellipse(150, 0, 50, 50);
popMatrix();

stroke(255, 255, 0);
strokeWeight(5);
point(50*loc+width/4, i++);
if (i>height)
{
i = 0;
background(0);
}
}

``````

1 Like

Hey @kll

Yes this is working. Okay, I get it - the ball has a kind of behaviour where it struggles to keep up against a kind of gravity pulling the ball back down. Nice, thanks for sharing this

My real goal is to achieve a sketch where:

• the ball registers the target

• the ball begins to accelerate towards the target (ideally with an acceleration curve/non linear)

• the ball is locked on to the target and can keep up with the target so long as the target doesn’t exceed the ball’s predetermined max speed

• once the ball looses the target the ball decelerates

I think I have a way to go!

1 Like

@glv, that’s really cool! Thanks for sharing

1 Like

thanks,
that sounds a little like my old

auto shooter !! NOT A GAME !!

actually my first PVector exercise

• search
• move ( speed like easing )
• shoot
• next target …
``````// https://discourse.processing.org/t/bug-with-arraylist/5949
// 11/2018 kll  / my first game ever, good its not really a game
// make a arraylist of pvector points
// calc shortest distance to the next target, aim, fire + remove the point from array

int idt, pmax = 10;
ArrayList<PVector> points = new ArrayList<PVector>();     // list of points
PVector target = new PVector(0, 0);                       // position of cross line
boolean theend = false, shoot = false;

int k = 0, kmax = 30;  //firing circles
float tdist, newdist, speedf = 0.04;   // speed factor multiplied with distance gives realistic targeting movement

void setup() {
size( 300, 300);
for (int i = 0; i < pmax; i++) points.add(new PVector(random(-width/2, width/2), random(-height/2, height/2)));
//for ( PVector p : points ) println(p);
println("targets: "+points.size());
}

void draw_lines() {
stroke(200, 0, 0);
line(target.x, -height/2, target.x, height/2);
line(-width/2, target.y, width/2, target.y);
}

void draw_points() {
for (int i = 0; i < points.size(); i++) {
if ( i == idt ) stroke(200, 0, 0);
else noStroke();
fill(0, 200, 0);
ellipse(points.get(i).x, points.get(i).y, 8, 8);
}
}

int get_nearest() {
tdist = width;                              // init to max
for (int i = 0; i < points.size(); i++) {
newdist = dist(points.get(i).x, points.get(i).y, target.x, target.y);
if ( newdist < tdist ) {
tdist = newdist;
idx = i;
}
}
return idx;                                 // give index of nearest target
}

void aim() {
if ( idt > -1 ) {
PVector waytotarget = points.get(idt).copy();
waytotarget.sub(target);
if ( waytotarget.mag() > 0.5 ) {                     // thats close enough
waytotarget.setMag(waytotarget.mag()*speedf);      // calc faster to short step in that direction
target = target.add(waytotarget);                  // change target
} else {
if ( !shoot ) println("shoot at "+idt);            // only print
shoot = true;
points.remove(idt);
}
} else {
println("i win, but now i am alone!");
theend = true;
}
}

void fire() {                                   // only if shoot = true
fill(200, 100, 0);
stroke(255, 0, 0);
strokeWeight(3);
ellipse(target.x, target.y, 2*k, 2*k);        // growing red circle
strokeWeight(1);
k++;
if ( k >= kmax ) {
shoot = false;
k = 0;
}
}

void show_end() {
fill(0, 200, 0);
rect(-50, -25, 100, 40);
fill(200, 0, 0);
text(" I WIN ! ", -20, 0);                    // well, what a wonder if you play alone and targets not move
}

void draw() {
background(200, 200, 0);
translate(width/2, height/2);
if ( !theend ) {
draw_lines();                               // cross lines
draw_points();                              // all points fix pos, same size / red stroke for next target
idt = get_nearest();                        // next ( nearest ) target
if ( !shoot )      aim();                   // not walk while firing
else               fire();                  // some show / red circles
} else show_end();                            // all over, no play again button
}

``````
1 Like

Haha, that’s great.

Interesting, I guess a very important point that is missing from the original sketch is first determining how far away the target is.

Although, that is kind of happening with dalpha no?

``````  if      ( dalpha < -1)       rot -= TWO_PI;  // @brunoruchiga
else if ( dalpha > 1 )       rot += TWO_PI;
rot += (alpha -rot) * easing_var;                // @phoebus
``````

If dalpha was more specifically targeted, that could be a way of simulating a different kind of acceleration?

if you talk about physical acceleration yes, both would be wrong

why not try @Chrisir s idea
of a sin speed acceleration-deceleration

Oops, totally missed his comment

here is sketch with lerp function

``````
float mousePosX=100, mousePosY=100,
mousePosXFinal=100, mousePosYFinal=100;

boolean startUp=false;

float amt;
float maxAmt = 0.066; // WAS 0.025

int i=0;
float initalDist1;

void setup() {
size(500, 500);
background(255);
frameRate(20);
}

void draw() {
background(255);

// show grid
for (int i = 0; i < width; i += 50) {
line(i, 0, i, height);
}
for (int i = 0; i < height; i += 50) {
line(0, i, width, i);
}

// move red rect
float dist1  = dist(mousePosX, mousePosY, mousePosXFinal, mousePosYFinal);
// Are we in the starting phase?
if (startUp  &&
amt<maxAmt &&
dist1>initalDist1/2) {   //
// Yes, slowly accelerate
amt += 0.0011;
} else { //  OR else if (amt>=0.025)
// No, we slowly decelerate
if (startUp)
println("hit");
startUp=false;
amt = map (dist1, 0, 900, 0.06, maxAmt);
}
ellipse(i++, height-(float)amt*1900, 5, 5);
mousePosX = lerp(mousePosX, mousePosXFinal, amt);
mousePosY = lerp(mousePosY, mousePosYFinal, amt);
if (dist1<0.029) {
if (amt>0)
println("Thank you");
amt=0;
mousePosX = mousePosXFinal;
mousePosY = mousePosYFinal;
}
// show red rect
fill(255, 0, 0);
rect(mousePosX, mousePosY, 50, 50);
text(amt, 31, 31);
}

// ---------------------------------------------------------

void mousePressed() {

// search mousePosXFinal
for (int i = 0; i < width; i += 50) {
if (mouseX<=i+50) {
mousePosXFinal = i;
break;
}
}
//
// search mousePosYFinal
for (int i = 0; i < height; i += 50) {
if (mouseY<=i+50) {
mousePosYFinal = i;
break;
}
}

// reset
startUp=true;
amt=0;
i=0;

float dist1  = dist(mousePosX, mousePosY, mousePosXFinal, mousePosYFinal);
println(dist1);
initalDist1=dist1;
}
//
``````
1 Like

Here is a another example: