(Inbe) Tweening : the process of generating intermediate frames - - PowerPoint PPT Presentation
(Inbe) Tweening : the process of generating intermediate frames - - PowerPoint PPT Presentation
(Inbe) Tweening : the process of generating intermediate frames between two key frames to give the appearance that the first evolves smoothly into the second. http://www.senocular.com http://en.wikipedia.org/wiki/Inbetweening Easing Functions :
Easing Functions : Mathematical functions used by tweening algorithms to calculate property values at time points between key frames Ease-in : The gradual shift of a tweened property from a stationary value to full animation Ease-out : The gradual shift of a tweened property from full animation to a stationary value. Easing can be applied to any numeric property
- x, y position
- width, height
- fill and line colors
- line width
…
Easing.pde
Ease-in Functions
N
Time Value
0.2 0.4 0.6 0.8 1 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 Fractional Value Change Normalized Time
Ease-in
Linear (N=1) Quadratic (N=2) Cubic (N=3) Quartic (N=4) Quintic (N=5)
EaseInRace.pde
Ease-out Functions
N
Time Value ) 1 ( 1
0.2 0.4 0.6 0.8 1 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 Fractonal Value Change Normalized Time
Ease-out
Linear (N=1) Quadratic (N=2) Qubic (N=3) Quartic (N=4) Quintic (N=5)
EaseOutRace.pde
Ease-in + Ease-out
- First half – Scaled Ease in
- Second half – Scaled and translated Ease out
0.2 0.4 0.6 0.8 1 0.2 0.4 0.6 0.8 1 Fractional Value Change Normalized Time
Ease-in + Ease-out
Linear Quadratic Qubic Quartic Quintic
EaseInOutRace.pde
Many other types of easing functions
– Sinusoidal Easing – Exponential Easing – Circular Easing – Oscillatory Easing (passes target and returns)
http://www.robertpenner.com/easing/easing_demo.html
Robert Penner's Easing Website
– Sample chapter on easing
Implementing Easing in Processing
- 1. Develop Bullseye class and draw
// BullsEye class BullsEye { float X; float Y; BullsEye(float _X, float _Y) { X = _X; Y = _Y; } // Draw BullsEye void draw() { pushMatrix(); translate(X, Y); scale(0.5,0.5); stroke(0); ellipseMode(CENTER); fill(0); ellipse(0,0,105,105); fill(255,0,0); ellipse(0,0,100,100); fill(255); ellipse(0,0,80,80); fill(255,0,0); ellipse(0,0,60,60); fill(255); ellipse(0,0,40,40); fill(0); noStroke(); ellipse(0,0,20,20); popMatrix(); } }
Easing1.pde
// Easing1 BullsEye be; void setup() { size(500, 500); smooth(); be = new BullsEye(250, 250); } void draw() { background(255); be.draw(); } void mousePressed() { be.X = mouseX; be.Y = mouseY; }
Easing1.pde
Implementing Easing in Processing
- 2. Build Linear Easing into BullsEye
// BullsEye class BullsEye { float X; float Y; // Parameters for easing float startV; float deltaV; float nSteps; float currentStep = 0.0; boolean easing = false; BullsEye(float _X, float _Y) { X = _X; Y = _Y; } // Incrememt location float next() { float nextV; // Increment ease step currentStep++; // Only ease within step range if (currentStep > nSteps) { currentStep = nSteps; nextV = startV + deltaV; easing = false; currentStep = 0; } else { float fracT = currentStep/nSteps; nextV = startV + deltaV * fracT; } return nextV; }
Easing2.pde
Implementing Easing in Processing
- 2. Build Linear Easing into BullsEye (2)
// Easing2 BullsEye be; void setup() { size(500, 500); smooth(); be = new BullsEye(250, 250); } void draw() { background(255); if (be.easing) be.X = be.next(); be.draw(); } void mousePressed() { be.startV = be.X; be.deltaV = mouseX-be.X; be.nSteps = 50; be.easing = true; }
Easing2.pde
Implementing Easing in Processing
- 3. Extract easing behavior into an Ease class
// Class encapsulating easing behavior class Ease { float startV; float deltaV; float nSteps; float currentStep = 0.0; Ease(float _startV, float _endV, float _nSteps) { startV = _startV; deltaV = _endV - _startV; nSteps = _nSteps; } float calcEase(float fracT) { return fracT; } // Incrememt location float next() { float nextV; // Increment ease step currentStep++; // Only ease within step range if (currentStep > nSteps) { currentStep = nSteps; nextV = startV + deltaV; } else { float fracT = currentStep/nSteps; nextV = startV + deltaV*calcEase(fracT); } return nextV; }
Easing3.pde
// BullsEye class BullsEye { float X; float Y; // Position easing objects Ease easeX = null; Ease easeY = null; BullsEye(float _X, float _Y) { X = _X; Y = _Y; } // Increment Easing, // if an Ease object is specified void next() { if (easeX != null) X = easeX.next(); if (easeY != null) Y = easeY.next(); } …
Implementing Easing in Processing
- 3. Extract easing behavior into an Ease class (2)
// Easing3 BullsEye be; void setup() { size(500, 500); smooth(); be = new BullsEye(250, 250); } void draw() { background(255); be.next(); be.draw(); } void mousePressed() { // Set easing in on mouse pressed be.easeX = new Ease(be.X, mouseX, 50); be.easeY = new Ease(be.Y, mouseY, 50); }
Now, both X and Y (or any field) can be eased
Easing3.pde
Implementing Easing in Processing
- 4. Create new kinds of Easing classes
Easing4.pde
// Class encapsulating quadratic easing class QuadEaseIn { float startV; float deltaV; float nSteps; float currentStep = 0.0; QuadEaseIn(float _startV, float _endV, float _nSteps) { startV = _startV; deltaV = _endV - _startV; nSteps = _nSteps; } float calcEase(float fracT) { return pow(fracT, 2); } // Incrememt location float next() { float nextV; // Increment ease step currentStep++; // Only ease within step range if (currentStep > nSteps) { currentStep = nSteps; nextV = startV + deltaV; } else { float fracT = currentStep/nSteps; nextV = startV + deltaV*calcEase(fracT); } return nextV; }
The only difference between Ease and QuadEaseIn is the name and part of the easing function
Implementing Easing in Processing
- 4. Create new kinds of Easing classes (2)
Easing4.pde
// BullsEye class BullsEye { float X; float Y; // Position easing objects QuadEaseIn easeX = null; QuadEaseIn easeY = null; BullsEye(float _X, float _Y) { X = _X; Y = _Y; } // Increment Easing, // if an Ease object is specified void next() { if (easeX != null) X = easeX.next(); if (easeY != null) Y = easeY.next(); } … // Easing3 BullsEye be; void setup() { size(500, 500); smooth(); be = new BullsEye(250, 250); } void draw() { background(255); be.next(); be.draw(); } void mousePressed() { // Set easing in on mouse pressed be.easeX = new QuadEaseIn(be.X, mouseX, 50); be.easeY = new QuadEaseIn(be.Y, mouseY, 50); }
- In fact, if we want to change the easing
behavior of the BullsEye class, we have to edit its source code every time!
- There has to be a better way …
… Hold that thought …
Object Oriented Programming
– Encapsulation
- Classes encapsulate state (fields) and behavior (methods)
– Polymorphism
- Signature Polymorphism – Overloading
- Subtype Polymorphism – Inheritance
Creating a set of Graphic Object Classes
- All have…
- X, Y location
- width and height fields
- fill and stroke colors
- A draw() method
- A next() method defining how they move
- …
- Implementation varies from class to class
Creating a set of Graphic Object Classes
- Problems
How would you hold all your objects? – Array? – ArrayList or HashMap? What if one class had extra methods or special arguments?
Sometimes you want to think of an object as a generic Graphic (X,Y location and draw() method) Sometimes you want to think of an object as a specific type (extra methods, extra fields, …)
Graphic Ellipse Rectangle Arc Curve Shape Circle Square
More General More Specific
Graphic Object Hierarchy
X,Y fields draw() method … diameter
Inheritance gives you a way to relate your objects in a hierarchical manner
Inheritance
- Superclass (base class) – higher in the hierarchy
- Subclass (child class) – lower in the hierarchy
- A subclass is derived from from a superclass
- Subclasses inherit the fields and methods of their
superclass.
– I.e. subclasses automatically "get" stuff in superclasses
- Subclasses can override a superclass method by
redefining it.
– They can replace anything by redefining locally
// Ellipse base class class Ellipse { float X; float Y; float W; float H; // Ellipses are always red color fillColor = color(255,0,0); Ellipse(float X, float Y, float W, float H) { this.X = X; this.Y = Y; this.W = W; this.H = H; } void draw() { ellipseMode(CENTER); fill(fillColor); ellipse(X, Y, W, H); } } // Circle derived class class Circle extends Ellipse { Circle(float X, float Y, float D) { super(X, Y, D, D); // Circles are always blue fillColor = color(0,255,0); } }
- The extends keyword creates
hierarchical relationship between classes.
- The Circle class gets all fields and
methods of the Ellipse class, automatically.
- The super keyword refers to the base
class in the relationship.
- The this keyword refers to the object
itself.
Graphics.pde
Graphics.pde
// Graphics Ellipse e = new Ellipse(150, 250, 150, 50); Circle c = new Circle(350, 250, 75); void setup() { size(500, 500); smooth(); } void draw() { e.draw(); c.draw(); }
// Graphics2 Ellipse[] e = new Ellipse[20]; void setup() { size(500, 500); smooth(); for (int i=0; i<e.length; i++) { float X = random(0, width); float Y = random(0, height); float W = random(10, 100); float H = random(10, 100); // Ellipses are Circles are // stored in the same array if (random(1.0) < 0.5) e[i] = new Ellipse(X,Y,W,H); else e[i] = new Circle(X,Y,W); } } void draw() { for (int i=0; i<e.length; i++) e[i].draw(); }
Ellipses and Circles in the same array!
Graphics2.pde
Graphics3.pde
// Ellipse base class class Ellipse { float X; float Y; float W; float H; // Ellipses are always red color fillColor = color(255,0,0); Ellipse(float X, float Y, float W, float H) { this.X = X; this.Y = Y; this.W = W; this.H = H; } void draw() { ellipseMode(CENTER); fill(fillColor); ellipse(X, Y, W, H); } // Do nothing void mousePressed() {} } // Circle derived class class Circle extends Ellipse { Circle(float X, float Y, float D) { super(X, Y, D, D); // Circles are always blue fillColor = color(0,255,0); } // Change color of circle when clicked void mousePressed() { if (dist(mouseX, mouseY, X, Y) < 0.5*W) fillColor = color(0,0,255); } }
- The mousePressed behavior of the
Circle class overrides the default behavior of the Ellipse class.
Graphics3.pde
// Graphics3 Ellipse[] e = new Ellipse[20]; void setup() { size(500, 500); smooth(); // Stuff removed … } void draw() { for (int i=0; i<e.length; i++) e[i].draw(); } void mousePressed() { for (int i=0; i<e.length; i++) e[i].mousePressed(); }
A few more rules about inheritance …
- A child’s constructor is responsible for calling
the parent’s constructor
- The first line of a child’s constructor should use
the super reference to call the parent’s constructor
- The super reference can also be used to
reference other variables and methods defined in the parent’s class
Use inheritance to solve our Ease class problem
– The BullsEye class should declare easeX and easeY as variables of type Ease. – New kinds of Ease classes can be defined as subclasses of Ease – Only the calcEase() function must be overridden.
Ease PolyEaseIn LinearEase PolyEaseOut PolyEaseInOut … Easing.pde
// Class encapsulating polynomial ease-in behavior class PolyEaseIn extends Ease { PolyEaseIn(float _startV, float _endV, float _nSteps, float _param) { super(_startV, _endV, _nSteps, _param); } float calcEase(float fracT) { return pow(fracT, param); } } // Class encapsulating polynomial ease-out behavior class PolyEaseOut extends Ease{ PolyEaseOut(float _startV, float _endV, float _nSteps, float _param) { super(_startV, _endV, _nSteps, _param); } float calcEase(float fracT) { return 1.0-pow(1.0-fracT, param); } } // Class encapsulating polynomial ease-in and ease-out behavior class PolyEaseInOut extends Ease { PolyEaseInOut(float _startV, float _endV, float _nSteps, float _param) { super(_startV, _endV, _nSteps, _param); } float calcEase(float fracT) { if (fracT < 0.5) return 0.5*pow(2.0*fracT, param); else return 0.5+0.5*(1.0-pow(1.0-(2.0*(fracT-0.5)), param)); } }
// BullsEye class BullsEye { float X; float Y; // Position easing objects Ease easeX = null; Ease easeY = null; BullsEye(float X, float Y) { this.X = X; this.Y = Y; } // Increment Easing void next() { if (easeX != null) X = easeX.next(); if (easeY != null) Y = easeY.next(); }
The BullsEye class can declare its easing objects using the more general type Ease…
// Easing BullsEye be; void setup() { size(500, 500); smooth(); be = new BullsEye(250, 250); } void draw() { background(255); be.next(); be.draw(); } void mousePressed() { // Set easing in on mouse pressed be.easeX = new LinearEase(be.X, mouseX, 50); be.easeY = new LinearEase(be.Y, mouseY, 50); // be.easeX = new PolyEaseInOut(be.X, mouseX, 50, 2); // be.easeY = new PolyEaseInOut(be.Y, mouseY, 50, 2); }
… but they can be assigned using
- ne of several more specific types
- f Ease.