diff --git a/compiled/radium.js b/compiled/radium.js
index f99bd85..689015a 100644
--- a/compiled/radium.js
+++ b/compiled/radium.js
@@ -21,6 +21,9 @@
this.createScene = __bind(this.createScene, this);
this.setPreloadScene = __bind(this.setPreloadScene, this);
this.setInitialScene = __bind(this.setInitialScene, this);
+ this.skipTimers = __bind(this.skipTimers, this);
+ this.updateTimers = __bind(this.updateTimers, this);
+ this.updateEasings = __bind(this.updateEasings, this);
this.iteration = __bind(this.iteration, this);
this.loop = __bind(this.loop, this);
this.start = __bind(this.start, this);
@@ -33,11 +36,15 @@
this.last_frameskip_collection = Math.floor(Date.now());
this.frameskip = 0;
this.current_frameskip = 0;
+ this.current_frame = 0;
this.scenes = {};
this.objects = {};
this.sounds = {};
this.sprites = {};
this.tilesets = {};
+ this.easings = [];
+ this.named_timers = {};
+ this.unnamed_timers = [];
}
Engine.prototype.addCanvas = function(canvas, label) {
@@ -79,15 +86,18 @@
};
Engine.prototype.iteration = function() {
- var belated_timeout, current_frame, frame_interval, name, next_frame, overtime, scene, _ref;
+ var belated_timeout, current_frame, frame_interval, name, next_frame, overtime, scene, skipped_frames, _ref;
frame_interval = 1000 / this.fps;
current_frame = Date.now();
next_frame = current_frame + frame_interval;
+ this.current_frame += 1;
if (Math.floor(current_frame) > this.last_frameskip_collection) {
this.frameskip = this.current_frameskip;
this.current_frameskip = 0;
this.last_frameskip_collection = Math.floor(current_frame);
}
+ this.updateEasings();
+ this.updateTimers();
_ref = this.scenes;
for (name in _ref) {
scene = _ref[name];
@@ -99,12 +109,89 @@
return setTimeout(this.iteration, next_frame - Date.now());
} else {
overtime = Date.now() - next_frame;
- this.current_frameskip += Math.floor(overtime / frame_interval);
+ skipped_frames = Math.floor(overtime / frame_interval);
+ this.current_frameskip += skipped_frames;
+ this.current_frame += skipped_frames;
+ this.skipTimers(skipped_frames);
belated_timeout = overtime % frame_interval;
return setTimeout(this.iteration, belated_timeout);
}
};
+ Engine.prototype.updateEasings = function() {
+ var easing, _i, _len, _ref;
+ _ref = this.easings;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ easing = _ref[_i];
+ if (this.current_frame >= (easing.start_frame + easing.duration)) {
+ if (easing.infinite) {
+ easing.start_frame = this.current_frame;
+ easing.updateValue(this.current_frame);
+ } else {
+ easing.finished = true;
+ easing.value = easing.end;
+ }
+ } else {
+ easing.updateValue(this.current_frame);
+ }
+ }
+ return this.easings = this.easings.filter(function(obj) {
+ return !obj.finished;
+ });
+ };
+
+ Engine.prototype.updateTimers = function() {
+ var key, timer, timer_name, val, _i, _len, _ref, _ref1, _results;
+ _ref = this.unnamed_timers.concat((function() {
+ var _ref, _results;
+ _ref = this.named_timers;
+ _results = [];
+ for (key in _ref) {
+ val = _ref[key];
+ _results.push(val);
+ }
+ return _results;
+ }).call(this));
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ timer = _ref[_i];
+ timer.step();
+ }
+ this.unnamed_timers = this.unnamed_timers.filter(function(obj) {
+ return !obj.finished;
+ });
+ _ref1 = this.named_timers;
+ _results = [];
+ for (timer_name in _ref1) {
+ timer = _ref1[timer_name];
+ if (timer.finished) {
+ _results.push(delete this.named_timers[timer_name]);
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ };
+
+ Engine.prototype.skipTimers = function(frames) {
+ var key, timer, val, _i, _len, _ref, _results;
+ _ref = this.unnamed_timers.concat((function() {
+ var _ref, _results1;
+ _ref = this.named_timers;
+ _results1 = [];
+ for (key in _ref) {
+ val = _ref[key];
+ _results1.push(val);
+ }
+ return _results1;
+ }).call(this));
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ timer = _ref[_i];
+ _results.push(timer.skip(frames));
+ }
+ return _results;
+ };
+
Engine.prototype.setInitialScene = function(scene) {
return this.initial_scene = scene;
};
@@ -196,19 +283,19 @@
})(this),
_finishPath: (function(_this) {
return function(surface, options) {
- var _ref, _ref1, _ref10, _ref11, _ref12, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
+ var _ref, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
if ((_ref = options.stroke) != null ? _ref : true) {
- surface.lineWidth = (_ref1 = (_ref2 = options.lineWidth) != null ? _ref2 : (_ref3 = options.pen) != null ? _ref3.lineWidth : void 0) != null ? _ref1 : 1;
- surface.strokeStyle = (_ref4 = (_ref5 = options.lineColor) != null ? _ref5 : (_ref6 = options.pen) != null ? _ref6.lineColor : void 0) != null ? _ref4 : "black";
- if ((_ref7 = options._is_text) != null ? _ref7 : false) {
+ surface.lineWidth = (_ref1 = (_ref2 = (_ref3 = (_ref4 = (_ref5 = options.lineWidth) != null ? _ref5.value : void 0) != null ? _ref4 : options.lineWidth) != null ? _ref3 : (_ref6 = options.pen) != null ? (_ref7 = _ref6.lineWidth) != null ? _ref7.value : void 0 : void 0) != null ? _ref2 : (_ref8 = options.pen) != null ? _ref8.lineWidth : void 0) != null ? _ref1 : 1;
+ surface.strokeStyle = (_ref9 = (_ref10 = options.lineColor) != null ? _ref10 : (_ref11 = options.pen) != null ? _ref11.lineColor : void 0) != null ? _ref9 : "black";
+ if ((_ref12 = options._is_text) != null ? _ref12 : false) {
surface.strokeText(options.text, options.x, options.y);
} else {
surface.stroke();
}
}
- if ((_ref8 = options.fill) != null ? _ref8 : false) {
- surface.fillStyle = (_ref9 = (_ref10 = options.fillColor) != null ? _ref10 : (_ref11 = options.pen) != null ? _ref11.fillColor : void 0) != null ? _ref9 : "white";
- if ((_ref12 = options._is_text) != null ? _ref12 : false) {
+ if ((_ref13 = options.fill) != null ? _ref13 : false) {
+ surface.fillStyle = (_ref14 = (_ref15 = options.fillColor) != null ? _ref15 : (_ref16 = options.pen) != null ? _ref16.fillColor : void 0) != null ? _ref14 : "white";
+ if ((_ref17 = options._is_text) != null ? _ref17 : false) {
return surface.fillText(options.text, options.x, options.y);
} else {
return surface.fill();
@@ -227,26 +314,31 @@
})(this),
_applyTextContext: (function(_this) {
return function(surface, options) {
- var font_family, font_size, font_style, font_weight, scale, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
+ var font_family, font_size, font_style, font_weight, scale, _ref, _ref1, _ref10, _ref11, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
font_family = (_ref = options.font) != null ? _ref : "sans-serif";
- font_size = (_ref1 = options.size) != null ? _ref1 : 16;
- font_weight = (_ref2 = options.weight) != null ? _ref2 : "normal";
- font_style = (_ref3 = options.style) != null ? _ref3 : "normal";
- scale = (_ref4 = options.scale) != null ? _ref4 : 1;
+ font_size = (_ref1 = (_ref2 = (_ref3 = options.size) != null ? _ref3.value : void 0) != null ? _ref2 : options.size) != null ? _ref1 : 16;
+ font_weight = (_ref4 = options.weight) != null ? _ref4 : "normal";
+ font_style = (_ref5 = options.style) != null ? _ref5 : "normal";
+ scale = (_ref6 = (_ref7 = (_ref8 = options.scale) != null ? _ref8.value : void 0) != null ? _ref7 : options.scale) != null ? _ref6 : 1;
surface.save();
surface.font = "" + font_weight + " " + font_style + " " + font_size + "px '" + font_family + "'";
- surface.globalAlpha = (_ref5 = options.alpha) != null ? _ref5 : 1;
+ surface.globalAlpha = (_ref9 = (_ref10 = (_ref11 = options.alpha) != null ? _ref11.value : void 0) != null ? _ref10 : options.alpha) != null ? _ref9 : 1;
return surface.scale(scale, scale);
};
})(this),
line: (function(_this) {
return function(x1, y1, x2, y2, options, surface) {
+ var _ref, _ref1, _ref2, _ref3;
if (options == null) {
options = {};
}
if (surface == null) {
surface = "";
}
+ x1 = (_ref = x1.value) != null ? _ref : x1;
+ y1 = (_ref1 = y1.value) != null ? _ref1 : y1;
+ x2 = (_ref2 = x2.value) != null ? _ref2 : x2;
+ y2 = (_ref3 = y2.value) != null ? _ref3 : y2;
surface = _this._startPath(surface, options);
surface.moveTo(x1, y1);
surface.lineTo(x2, y2);
@@ -255,12 +347,17 @@
})(this),
rectangle: (function(_this) {
return function(x1, y1, x2, y2, options, surface) {
+ var _ref, _ref1, _ref2, _ref3;
if (options == null) {
options = {};
}
if (surface == null) {
surface = "";
}
+ x1 = (_ref = x1.value) != null ? _ref : x1;
+ y1 = (_ref1 = y1.value) != null ? _ref1 : y1;
+ x2 = (_ref2 = x2.value) != null ? _ref2 : x2;
+ y2 = (_ref3 = y2.value) != null ? _ref3 : y2;
surface = _this._startPath(surface, options);
surface.rect(x1, y1, x2 - x1, y2 - y1);
return _this._finishPath(surface, options);
@@ -268,13 +365,17 @@
})(this),
boxEllipse: (function(_this) {
return function(x1, y1, x2, y2, options, surface) {
- var rx, ry, x, y;
+ var rx, ry, x, y, _ref, _ref1, _ref2, _ref3;
if (options == null) {
options = {};
}
if (surface == null) {
surface = "";
}
+ x1 = (_ref = x1.value) != null ? _ref : x1;
+ y1 = (_ref1 = y1.value) != null ? _ref1 : y1;
+ x2 = (_ref2 = x2.value) != null ? _ref2 : x2;
+ y2 = (_ref3 = y2.value) != null ? _ref3 : y2;
x = (x1 + x2) / 2;
y = (y1 + y2) / 2;
rx = (x2 - x1) / 2;
@@ -284,20 +385,24 @@
})(this),
radiusEllipse: (function(_this) {
return function(x, y, rx, ry, options, surface) {
- var i, step, _i, _ref, _ref1;
+ var i, step, _i, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
if (options == null) {
options = {};
}
if (surface == null) {
surface = "";
}
+ x = (_ref = x.value) != null ? _ref : x;
+ y = (_ref1 = y.value) != null ? _ref1 : y;
+ rx = (_ref2 = rx.value) != null ? _ref2 : rx;
+ ry = (_ref3 = ry.value) != null ? _ref3 : ry;
surface = _this._startPath(surface, options);
- step = (_ref = options.step) != null ? _ref : 0.1;
+ step = (_ref4 = (_ref5 = (_ref6 = options.step) != null ? _ref6.value : void 0) != null ? _ref5 : options.step) != null ? _ref4 : 0.1;
if (rx === ry) {
surface.arc(x, y, rx, 0, 2 * Math.PI, false);
} else {
surface.moveTo(x + rx, y);
- for (i = _i = 0, _ref1 = Math.PI * 2 + step; 0 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 0 <= _ref1 ? ++_i : --_i) {
+ for (i = _i = 0, _ref7 = Math.PI * 2 + step; 0 <= _ref7 ? _i <= _ref7 : _i >= _ref7; i = 0 <= _ref7 ? ++_i : --_i) {
surface.lineTo(x + (Math.cos(i) * rx), y + (Math.sin(i) * ry));
}
}
@@ -328,19 +433,19 @@
})(this),
text: (function(_this) {
return function(x, y, text, options, surface) {
- var text_width;
+ var text_width, _ref, _ref1, _ref2, _ref3, _ref4;
if (options == null) {
options = {};
}
if (surface == null) {
surface = "";
}
+ x = (_ref = x.value) != null ? _ref : x;
+ y = (_ref1 = y.value) != null ? _ref1 : y;
if (options.alignment == null) {
options.alignment = "left";
}
- if (options.scale == null) {
- options.scale = 1;
- }
+ options.scale = (_ref2 = (_ref3 = (_ref4 = options.scale) != null ? _ref4.value : void 0) != null ? _ref3 : options.scale) != null ? _ref2 : 1;
options._is_text = true;
options.text = text;
options.y = y;
diff --git a/easing.html b/easing.html
new file mode 100644
index 0000000..d61a772
--- /dev/null
+++ b/easing.html
@@ -0,0 +1,14 @@
+
+
+
+ Easing Demo
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/radium/engine.coffee b/radium/engine.coffee
index 025717a..89e4fea 100644
--- a/radium/engine.coffee
+++ b/radium/engine.coffee
@@ -7,12 +7,16 @@ class Engine
@last_frameskip_collection = Math.floor(Date.now())
@frameskip = 0
@current_frameskip = 0
+ @current_frame = 0
@scenes = {}
@objects = {}
@sounds = {}
@sprites = {}
@tilesets = {}
+ @easings = []
+ @named_timers = {}
+ @unnamed_timers = []
addCanvas: (canvas, label = "") =>
@canvases[label] = util.unpackElement(canvas)
@@ -48,12 +52,19 @@ class Engine
current_frame = Date.now()
next_frame = current_frame + frame_interval
+ @current_frame += 1
+
if Math.floor(current_frame) > @last_frameskip_collection
@frameskip = @current_frameskip
@current_frameskip = 0
@last_frameskip_collection = Math.floor(current_frame)
- # Actual iteration code
+ # Actual iteration code...
+ # First update the registered easings.
+ @updateEasings()
+ # Then process registered timers.
+ @updateTimers()
+ # Now we run the scene-specific code.
scene.iteration() for name, scene of @scenes when scene.active
# Frameskip check and triggering next iteration
@@ -62,10 +73,43 @@ class Engine
else
# Frameskip!
overtime = Date.now() - next_frame
- @current_frameskip += Math.floor(overtime / frame_interval)
+ skipped_frames = Math.floor(overtime / frame_interval)
+ @current_frameskip += skipped_frames
+ @current_frame += skipped_frames
+ @skipTimers(skipped_frames)
belated_timeout = overtime % frame_interval
setTimeout(@iteration, belated_timeout)
+ updateEasings: =>
+ for easing in @easings
+ if @current_frame >= (easing.start_frame + easing.duration)
+ if easing.infinite
+ easing.start_frame = @current_frame
+ easing.updateValue(@current_frame)
+ else
+ easing.finished = true
+ easing.value = easing.end
+ else
+ easing.updateValue(@current_frame)
+
+ # Clean up finished easings
+ @easings = @easings.filter (obj) -> not obj.finished
+
+ updateTimers: =>
+ for timer in @unnamed_timers.concat (val for key, val of @named_timers)
+ timer.step()
+
+ # Clean up finished timers
+ @unnamed_timers = @unnamed_timers.filter (obj) -> not obj.finished
+
+ for timer_name, timer of @named_timers
+ if timer.finished
+ delete @named_timers[timer_name]
+
+ skipTimers: (frames) =>
+ for timer in @unnamed_timers.concat (val for key, val of @named_timers)
+ timer.skip(frames)
+
setInitialScene: (scene) =>
@initial_scene = scene
diff --git a/radium/engine.draw.coffee b/radium/engine.draw.coffee
index 0f2a400..2114cf7 100644
--- a/radium/engine.draw.coffee
+++ b/radium/engine.draw.coffee
@@ -9,7 +9,7 @@ Engine::draw =
_finishPath: (surface, options) =>
if options.stroke ? true
- surface.lineWidth = options.lineWidth ? options.pen?.lineWidth ? 1
+ surface.lineWidth = options.lineWidth?.value ? options.lineWidth ? options.pen?.lineWidth?.value ? options.pen?.lineWidth ? 1
surface.strokeStyle = options.lineColor ? options.pen?.lineColor ? "black"
if options._is_text ? false
@@ -33,28 +33,46 @@ Engine::draw =
_applyTextContext: (surface, options) =>
font_family = options.font ? "sans-serif"
- font_size = options.size ? 16
+ font_size = options.size?.value ? options.size ? 16
font_weight = options.weight ? "normal"
font_style = options.style ? "normal"
- scale = options.scale ? 1
+ scale = options.scale?.value ? options.scale ? 1
surface.save()
surface.font = "#{font_weight} #{font_style} #{font_size}px '#{font_family}'"
- surface.globalAlpha = options.alpha ? 1
+ surface.globalAlpha = options.alpha?.value ? options.alpha ? 1
surface.scale(scale, scale)
line: (x1, y1, x2, y2, options = {}, surface = "") =>
+ # Ease-able properties
+ x1 = x1.value ? x1
+ y1 = y1.value ? y1
+ x2 = x2.value ? x2
+ y2 = y2.value ? y2
+
surface = @_startPath(surface, options)
surface.moveTo(x1, y1)
surface.lineTo(x2, y2)
@_finishPath(surface, options)
rectangle: (x1, y1, x2, y2, options = {}, surface = "") =>
+ # Ease-able properties
+ x1 = x1.value ? x1
+ y1 = y1.value ? y1
+ x2 = x2.value ? x2
+ y2 = y2.value ? y2
+
surface = @_startPath(surface, options)
surface.rect(x1, y1, x2 - x1, y2 - y1)
@_finishPath(surface, options)
boxEllipse: (x1, y1, x2, y2, options = {}, surface = "") =>
+ # Ease-able properties
+ x1 = x1.value ? x1
+ y1 = y1.value ? y1
+ x2 = x2.value ? x2
+ y2 = y2.value ? y2
+
x = (x1 + x2) / 2;
y = (y1 + y2) / 2;
rx = (x2 - x1) / 2;
@@ -62,9 +80,15 @@ Engine::draw =
@radiusEllipse(x, y, rx, ry, options, surface)
radiusEllipse: (x, y, rx, ry, options = {}, surface = "") =>
+ # Ease-able properties
+ x = x.value ? x
+ y = y.value ? y
+ rx = rx.value ? rx
+ ry = ry.value ? ry
+
surface = @_startPath(surface, options)
- step = options.step ? 0.1
+ step = options.step?.value ? options.step ? 0.1
if rx == ry
surface.arc(x, y, rx, 0, 2 * Math.PI, false)
@@ -83,9 +107,13 @@ Engine::draw =
pass # TODO
text: (x, y, text, options = {}, surface = "") =>
+ # Ease-able properties
+ x = x.value ? x
+ y = y.value ? y
+
# Defaults
options.alignment ?= "left"
- options.scale ?= 1
+ options.scale = options.scale?.value ? options.scale ? 1
options._is_text = true
options.text = text
options.y = y
diff --git a/radium/engine.ease.coffee b/radium/engine.ease.coffee
new file mode 100644
index 0000000..77f30e5
--- /dev/null
+++ b/radium/engine.ease.coffee
@@ -0,0 +1,165 @@
+Engine::ease =
+ _calculateElasticValues: (amplitude, period, change, inout = false) =>
+ if !period?
+ if inout
+ period = duration * (0.3 * 1.5)
+ else
+ period = duration * 0.3
+
+ if !amplitude? or amplitude < Math.abs(change)
+ amplitude = change
+ overshoot = period / 4
+ else
+ overshoot = period / (2 * Math.PI) * Math.asin(change / amplitude)
+
+ return [amplitude, period, change]
+
+ backIn: (start, end, duration, infinite = false, overshoot = 1.70158) =>
+ return new Ease("backIn", infinite, start, end, @current_frame, duration, overshoot)
+ backOut: (start, end, duration, infinite = false, overshoot = 1.70158) =>
+ return new Ease("backOut", infinite, start, end, @current_frame, duration, overshoot)
+ backInOut: (start, end, duration, infinite = false, overshoot = 1.70158) =>
+ return new Ease("backInOut", infinite, start, end, @current_frame, duration, overshoot)
+ bounceOut: (start, end, duration, infinite = false) =>
+ return new Ease("bounceOut", infinite, start, end, @current_frame, duration)
+ bounceIn: (start, end, duration, infinite = false) =>
+ return new Ease("bounceIn", infinite, start, end, @current_frame, duration)
+ bounceInOut: (start, end, duration, infinite = false) =>
+ return new Ease("bounceInOut", infinite, start, end, @current_frame, duration)
+ circOut: (start, end, duration, infinite = false) =>
+ return new Ease("circOut", infinite, start, end, @current_frame, duration)
+ circIn: (start, end, duration, infinite = false) =>
+ return new Ease("circIn", infinite, start, end, @current_frame, duration)
+ circInOut: (start, end, duration, infinite = false) =>
+ return new Ease("circInOut", infinite, start, end, @current_frame, duration)
+ cubicOut: (start, end, duration, infinite = false) =>
+ return new Ease("cubicOut", infinite, start, end, @current_frame, duration)
+ cubicIn: (start, end, duration, infinite = false) =>
+ return new Ease("cubicIn", infinite, start, end, @current_frame, duration)
+ cubicInOut: (start, end, duration, infinite = false) =>
+ return new Ease("cubicInOut", infinite, start, end, @current_frame, duration)
+ elasticOut: (start, end, duration, infinite = false, amplitude = null, period = null) =>
+ [amplitude, period, change] = @_calculateElasticValues(amplitude, period, end - start)
+ end = start + change
+ return new Ease("elasticOut", infinite, start, end, @current_frame, duration)
+ elasticIn: (start, end, duration, infinite = false, amplitude = null, period = null) =>
+ [amplitude, period, change] = @_calculateElasticValues(amplitude, period, end - start)
+ end = start + change
+ return new Ease("elasticIn", infinite, start, end, @current_frame, duration)
+ elasticInOut: (start, end, duration, infinite = false, amplitude = null, period = null) =>
+ [amplitude, period, change] = @_calculateElasticValues(amplitude, period, end - start, true)
+ end = start + change
+ return new Ease("elasticInOut", infinite, start, end, @current_frame, duration)
+ expoOut: (start, end, duration, infinite = false) =>
+ return new Ease("expoOut", infinite, start, end, @current_frame, duration)
+ expoIn: (start, end, duration, infinite = false) =>
+ return new Ease("expoIn", infinite, start, end, @current_frame, duration)
+ expoInOut: (start, end, duration, infinite = false) =>
+ return new Ease("expoInOut", infinite, start, end, @current_frame, duration)
+ linearNone: (start, end, duration, infinite = false) =>
+ return new Ease("linearNone", infinite, start, end, @current_frame, duration)
+ linearOut: (start, end, duration, infinite = false) =>
+ return new Ease("linearNone", infinite, start, end, @current_frame, duration)
+ linearIn: (start, end, duration, infinite = false) =>
+ return new Ease("linearNone", infinite, start, end, @current_frame, duration)
+ linearInOut: (start, end, duration, infinite = false) =>
+ return new Ease("linearNone", infinite, start, end, @current_frame, duration)
+ quadOut: (start, end, duration, infinite = false) =>
+ return new Ease("quadOut", infinite, start, end, @current_frame, duration)
+ quadIn: (start, end, duration, infinite = false) =>
+ return new Ease("quadIn", infinite, start, end, @current_frame, duration)
+ quadInOut: (start, end, duration, infinite = false) =>
+ return new Ease("quadInOut", infinite, start, end, @current_frame, duration)
+ quartOut: (start, end, duration, infinite = false) =>
+ return new Ease("quartOut", infinite, start, end, @current_frame, duration)
+ quartIn: (start, end, duration, infinite = false) =>
+ return new Ease("quartIn", infinite, start, end, @current_frame, duration)
+ quartInOut: (start, end, duration, infinite = false) =>
+ return new Ease("quartInOut", infinite, start, end, @current_frame, duration)
+ sineOut: (start, end, duration, infinite = false) =>
+ return new Ease("sineOut", infinite, start, end, @current_frame, duration)
+ sineIn: (start, end, duration, infinite = false) =>
+ return new Ease("sineIn", infinite, start, end, @current_frame, duration)
+ sineInOut: (start, end, duration, infinite = false) =>
+ return new Ease("sineInOut", infinite, start, end, @current_frame, duration)
+
+
+class Ease
+ # Port based on https://github.com/jimjeffers/Easie. I don't think this qualifies as a "bad thing" :)
+ constructor: (type, @infinite, @start, end, @start_frame, @duration, @params...) ->
+ @func = this[type]
+ @change = end - @start
+ @value = @start
+ @finished = false
+ # TODO: Investigate whether JS engines cache deterministic outcomes by themselves. If not,
+ # the below could provide some performance gain.
+ #@bounce_constant_1 = 1 / 2.75
+ #@bounce_constant_2 = 2 / 2.75
+ #@bounce_constant_3 = 2.5 / 2.75
+
+ updateValue: (current_frame) =>
+ @value = @func(current_frame - @start_frame)
+
+ backIn: (time) =>
+ time = time / @duration
+ overshoot = @params[0]
+ return @change * time * time * ((overshoot + 1) * time - overshoot) + @start
+
+ backOut: (time) =>
+ time = time / @duration - 1
+ overshoot = @params[0]
+ return @change * (time * time * ((overshoot + 1) * time + overshoot) + 1) + @start
+
+ backInOut: (time) =>
+ time = time / (@duration / 2)
+ overshoot = @params[0] * 1.525
+
+ if time < 1
+ return @change / 2 * (time * time * ((overshoot + 1) * time - overshoot)) + @start
+ else
+ time -= 2
+ return @change / 2 * (time * time * ((overshoot + 1) * time + overshoot) + 2) + @start
+
+ bounceOut: (time, start = null) =>
+ time = time / @duration
+ start = start ? @start
+
+ if time < 1 / 2.75
+ return @change * (7.5625 * time * time) + start
+ else if time < 2 / 2.75
+ time = time - (1.5 / 2.75)
+ return @change * (7.5625 * time * time + 0.75) + start
+ else if time < 2.5 / 2.75
+ time = time - (2.25 / 2.75)
+ return @change * (7.5625 * time * time + 0.9375) + start
+ else
+ time = time - (2.625 / 2.75)
+ return @change * (7.5625 * time * time + 0.984375) + start
+
+ bounceIn: (time, start = null) =>
+ start = start ? @start
+ return @change - @bounceOut(@duration - time, 0) + start
+
+ bounceInOut: (time) =>
+ if time < @duration / 2
+ return @bounceIn(time * 2, 0) + @start
+ else
+ return @bounceOut(time * 2 - @duration, 0) + @start
+
+ circIn: (time) =>
+ time = time / @duration
+ return -@change * (Math.sqrt(1 - time * time) - 1) + @start
+
+ circOut: (time) =>
+ time = time / @duration - 1
+ return @change * Math.sqrt(1 - time * time) + @start
+
+ circInOut: (time) =>
+ time = time / (@duration / 2)
+
+ if time < 1
+ return -@change / 2 * (Math.sqrt(1 - time * time) - 1) + @start
+ else
+ time = time - 2
+ return @change / 2 * (Math.sqrt(1 - time * time) + 1) + @begin
+
\ No newline at end of file
diff --git a/radium/engine.timing.coffee b/radium/engine.timing.coffee
new file mode 100644
index 0000000..cd129a7
--- /dev/null
+++ b/radium/engine.timing.coffee
@@ -0,0 +1,30 @@
+Engine::timing =
+ startTimer: (frames, callback, name = null, repeat = false) =>
+ timer = new Timer(frames, callback, repeat)
+ if name?
+ @named_timers[name] = timer
+ else
+ @unnamed_timers.push(timer)
+
+ stopTimer: (name)
+ @timers[name].stop()
+
+class Timer
+ constructor: (@frames, @callback, @repeat) =>
+ @current_frame = 0
+ @finished = false
+
+ step: =>
+ if @current_frame >= @frames
+ @callback()
+
+ if repeat
+ @current_frame = 0
+ else
+ @finished = true
+
+ skip: (frames) =>
+ @current_frame += frames
+
+ stop: =>
+ @finished = true
\ No newline at end of file