├── .npmignore ├── src ├── Point.ts ├── utils.ts ├── List.ts ├── types.ts ├── intersection.ts ├── parametric.ts ├── helper.ts ├── ctx-get-transform.ts ├── zip.ts └── animaxe.ts ├── types ├── svg-path-parser.d.ts ├── ctx-get-transform.d.ts ├── gl-mat3.d.ts ├── seedrandom.d.ts └── should.d.ts ├── .DS_Store ├── images ├── skew.gif ├── svg.gif ├── arrows.gif ├── svg-ref.gif ├── example0.gif ├── example1.gif ├── example2.gif ├── example3.gif ├── example4.gif ├── example5.gif ├── example6.gif ├── example7.gif ├── lissajous.gif ├── skew-ref.gif ├── arrows-ref.gif ├── elliptical.gif ├── example0-ref.gif ├── example1-ref.gif ├── example2-ref.gif ├── example3-ref.gif ├── example4-ref.gif ├── example5-ref.gif ├── example6-ref.gif ├── example7-ref.gif ├── readme_order.gif ├── elliptical-ref.gif ├── example4glitch.gif ├── lissajous-ref.gif ├── rainbow_sines.gif ├── rainbow_sines-ref.gif └── example4whitelight.gif ├── docs └── assets │ ├── images │ ├── icons.png │ ├── widgets.png │ ├── icons@2x.png │ └── widgets@2x.png │ └── js │ └── search.js ├── .vscode └── settings.json ├── peg ├── svg.d.ts └── svg.peg ├── dist ├── src │ ├── Point.js │ ├── experiment.js │ ├── utils.js │ ├── types.js │ ├── List.js │ ├── intersection.js │ ├── parametric.js │ └── helper.js ├── examples │ ├── skew.js │ ├── rainbow_sines.js │ ├── example3.js │ ├── readme_order.js │ ├── example4.js │ ├── example2.js │ ├── parametric.js │ ├── lissajous.js │ ├── example5.js │ ├── example6.js │ └── example1.js └── test │ ├── example0.js │ ├── skew.js │ ├── elliptical.js │ ├── svg.js │ └── readme_order.js ├── test └── example.template.ts ├── examples ├── example0.ts ├── skew.ts ├── elliptical.ts ├── svg.ts ├── example4.ts ├── example3.ts ├── rainbow_sines.ts ├── readme_order.ts ├── example1.ts ├── example2.ts ├── example5.ts ├── example7.ts ├── lissajous.ts ├── example6.ts └── arrows.ts ├── .gitignore ├── scripts └── extractExampleCode.js ├── tsconfig.json ├── LICENSE ├── html ├── scrap.html ├── parametric.html ├── lissajous_color.html ├── svg.html ├── skew.html ├── arrows.html ├── example0.html ├── example1.html ├── example2.html ├── example3.html ├── example4.html ├── example5.html ├── example6.html ├── example7.html ├── lissajous.html ├── elliptical.html ├── example.template.html ├── readme_order.html ├── rainbow_sines.html └── index.html ├── package.json └── gulpfile.js /.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Point.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /types/svg-path-parser.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/.DS_Store -------------------------------------------------------------------------------- /images/skew.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/skew.gif -------------------------------------------------------------------------------- /images/svg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/svg.gif -------------------------------------------------------------------------------- /images/arrows.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/arrows.gif -------------------------------------------------------------------------------- /images/svg-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/svg-ref.gif -------------------------------------------------------------------------------- /images/example0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example0.gif -------------------------------------------------------------------------------- /images/example1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example1.gif -------------------------------------------------------------------------------- /images/example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example2.gif -------------------------------------------------------------------------------- /images/example3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example3.gif -------------------------------------------------------------------------------- /images/example4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example4.gif -------------------------------------------------------------------------------- /images/example5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example5.gif -------------------------------------------------------------------------------- /images/example6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example6.gif -------------------------------------------------------------------------------- /images/example7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example7.gif -------------------------------------------------------------------------------- /images/lissajous.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/lissajous.gif -------------------------------------------------------------------------------- /images/skew-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/skew-ref.gif -------------------------------------------------------------------------------- /images/arrows-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/arrows-ref.gif -------------------------------------------------------------------------------- /images/elliptical.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/elliptical.gif -------------------------------------------------------------------------------- /images/example0-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example0-ref.gif -------------------------------------------------------------------------------- /images/example1-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example1-ref.gif -------------------------------------------------------------------------------- /images/example2-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example2-ref.gif -------------------------------------------------------------------------------- /images/example3-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example3-ref.gif -------------------------------------------------------------------------------- /images/example4-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example4-ref.gif -------------------------------------------------------------------------------- /images/example5-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example5-ref.gif -------------------------------------------------------------------------------- /images/example6-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example6-ref.gif -------------------------------------------------------------------------------- /images/example7-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example7-ref.gif -------------------------------------------------------------------------------- /images/readme_order.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/readme_order.gif -------------------------------------------------------------------------------- /images/elliptical-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/elliptical-ref.gif -------------------------------------------------------------------------------- /images/example4glitch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example4glitch.gif -------------------------------------------------------------------------------- /images/lissajous-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/lissajous-ref.gif -------------------------------------------------------------------------------- /images/rainbow_sines.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/rainbow_sines.gif -------------------------------------------------------------------------------- /docs/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/docs/assets/images/icons.png -------------------------------------------------------------------------------- /images/rainbow_sines-ref.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/rainbow_sines-ref.gif -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | 4 | } -------------------------------------------------------------------------------- /docs/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/docs/assets/images/widgets.png -------------------------------------------------------------------------------- /images/example4whitelight.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/images/example4whitelight.gif -------------------------------------------------------------------------------- /peg/svg.d.ts: -------------------------------------------------------------------------------- 1 | // svg.d.ts 2 | declare function parse(text: string): any[]; 3 | export = { 4 | parse: parse 5 | }; -------------------------------------------------------------------------------- /docs/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/docs/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlarkworthy/animaxe/HEAD/docs/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /docs/assets/js/search.js: -------------------------------------------------------------------------------- 1 | var typedoc = typedoc || {};typedoc.search = typedoc.search || {};typedoc.search.data = {"kinds":{},"rows":[]}; -------------------------------------------------------------------------------- /types/ctx-get-transform.d.ts: -------------------------------------------------------------------------------- 1 | interface CanvasRenderingContext2D { 2 | getTransform(): [number, number, number, number, number, number, number, number, number]; 3 | } -------------------------------------------------------------------------------- /dist/src/Point.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJzcmMvUG9pbnQuanMiLCJzb3VyY2VzQ29udGVudCI6W10sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 4 | -------------------------------------------------------------------------------- /types/gl-mat3.d.ts: -------------------------------------------------------------------------------- 1 | declare module "gl-mat3" { 2 | export function clone(...params: any[]) 3 | export function scale(...params: any[]) 4 | export function translate(...params: any[]) 5 | export function rotate(...params: any[]) 6 | export function multiply(...params: any[]) 7 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function arrayInitialize(n: number, factory: () => T): T[] { 2 | var arr = new Array(n); 3 | for (let i = 0; i < n; i++) arr[i] = factory(); 4 | return arr; 5 | } 6 | 7 | export function isFunction(x: any): boolean { 8 | return typeof x == 'function'; 9 | } 10 | -------------------------------------------------------------------------------- /test/example.template.ts: -------------------------------------------------------------------------------- 1 | // THIS IS AUTO GENERATED TEST CODE, DO NOT MODIFY DIRECTLY 2 | /// 3 | /// 4 | /// 5 | require('source-map-support').install(); 6 | require("should"); 7 | 8 | <%= content %> 9 | describe('<%= name %>', function () { 10 | it ('should match the reference', function(done) { 11 | helper.sameExample("<%= name %>", "<%= name %>-ref", function(equal) { 12 | equal.should.equal(true); 13 | done(); 14 | }) 15 | }); 16 | }); -------------------------------------------------------------------------------- /examples/example0.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(); 8 | 9 | //each frame, first draw black background to erase the previous contents 10 | animator.play(Ax.create().fillStyle("#FF0000").fillRect([0,0],[100,100])); 11 | 12 | // the helper function pipes injects the context, either from a web canvas or a fake node.js one. 13 | helper.playExample("@name", 1, animator, 100, 100); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | src/*.js 4 | src/*.js.map 5 | test/*.js 6 | test/*.js.map 7 | 8 | # Logs 9 | logs 10 | *.log 11 | 12 | compiled/ 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (http://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directory 34 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 35 | node_modules 36 | -------------------------------------------------------------------------------- /scripts/extractExampleCode.js: -------------------------------------------------------------------------------- 1 | // through2 is a thin wrapper around node transform streams 2 | var through = require('through2'); 3 | var gutil = require('gulp-util'); 4 | var PluginError = gutil.PluginError; 5 | 6 | function extractExampleCode() { 7 | // Creating a stream through which each file will pass 8 | return through.obj(function(file, enc, cb) { 9 | 10 | var contents = file.contents.toString(); 11 | var header = contents.indexOf("@header") + "@header".length; 12 | var start = contents.indexOf("@start") + "@start".length; 13 | var end = contents.indexOf("@end"); 14 | 15 | file.contents = new Buffer(contents.substring(0, header) + contents.substring(start, end)); 16 | 17 | cb(null, file); 18 | }); 19 | 20 | } 21 | 22 | // Exporting the plugin main function 23 | module.exports = extractExampleCode; -------------------------------------------------------------------------------- /src/List.ts: -------------------------------------------------------------------------------- 1 | import * as OT from "./frp" 2 | import * as types from "./types" 3 | export * from "./types" 4 | import * as Parameter from "./Parameter" 5 | 6 | type Tick = OT.BaseTick; 7 | 8 | export class List extends OT.SignalFn{ 9 | constructor(public attach: (upstream: Rx.Observable) => Rx.Observable) { 10 | super(attach); 11 | } 12 | 13 | mapElement(mapFn: (V) => Out): List { 14 | return new List(this.mapValue((vals: V[]) => vals.map(mapFn)).attach); 15 | } 16 | 17 | slice(start: types.NumberArg, end: types.NumberArg) { 18 | this.combine( 19 | () => (vals: V[], start: number, end: number) => 20 | vals.slice(start, end), 21 | Parameter.from(start), 22 | Parameter.from(end) 23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /dist/examples/skew.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(100, 100); 5 | //each frame, first draw black background to erase the previous contents 6 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 7 | // we draw single pixels of different hues moving on a circle circumference 8 | animator.play(Ax.create().parallel(Ax.range(0, 5).map(function (offset) { return Ax.create() 9 | .skewT(offset * 0.1) 10 | .translate(Parameter.point(Parameter.sin(Parameter.t()).mapValue(function (x) { return 45 * (x + 1); }), Parameter.cos(Parameter.t()).mapValue(function (x) { return 45 * (x + 1); }))) 11 | .fillStyle("white").fillRect([-1, -1], [3, 3]); }))); 12 | helper.playExample("@name", 20, animator, 100, 100); 13 | -------------------------------------------------------------------------------- /dist/src/experiment.js: -------------------------------------------------------------------------------- 1 | // the >>> function 2 | function pipe(a, b) { 3 | return function (x) { return b(a(x)); }; 4 | } 5 | // the arr lift 6 | /* 7 | function lift(fn: Function1): SignalTransformer { 8 | return (input: SignalFn) => >((time: Time) => fn(input(time))); 9 | }*/ 10 | // & combinator 11 | // Typing we need a SignalTransformer with a custom API, with methods attached 12 | 13 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9leHBlcmltZW50LnRzIl0sIm5hbWVzIjpbInBpcGUiXSwibWFwcGluZ3MiOiJBQWNBLG1CQUFtQjtBQUNuQixjQUFzQixDQUF5QixFQUFFLENBQXlCO0lBQ3RFQSxNQUFNQSxDQUFDQSxVQUFDQSxDQUFjQSxJQUFLQSxPQUFBQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxFQUFQQSxDQUFPQSxDQUFBQTtBQUN0Q0EsQ0FBQ0E7QUFFRCxlQUFlO0FBQ2Y7OztHQUdHO0FBRUgsZUFBZTtBQUVmLDhFQUE4RSIsImZpbGUiOiJzcmMvZXhwZXJpbWVudC5qcyIsInNvdXJjZXNDb250ZW50IjpbbnVsbF0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "dist" 6 | }, 7 | "files": [ 8 | "./src/zip.ts", 9 | "./src/svg.ts", 10 | "./src/animaxe.ts", 11 | "./src/canvas.ts", 12 | "./src/ctx-get-transform.ts", 13 | "./src/events.ts", 14 | "./src/glow.ts", 15 | "./src/List.ts", 16 | "./src/helper.ts", 17 | "./src/frp", 18 | "./src/Parameter.ts", 19 | "./src/parametric.ts", 20 | "./src/types.ts", 21 | "./examples/example1.ts", 22 | "./examples/example2.ts", 23 | "./examples/example3.ts", 24 | "./examples/example4.ts", 25 | "./examples/example5.ts", 26 | "./examples/example6.ts", 27 | "./examples/rainbow_sines", 28 | "./examples/skew.ts", 29 | "./examples/lissajous", 30 | "./examples/readme_order.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /examples/skew.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 8 | 9 | //each frame, first draw black background to erase the previous contents 10 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 11 | 12 | // we draw single pixels of different hues moving on a circle circumference 13 | animator.play( 14 | Ax.create().parallel( 15 | Ax.range(0, 5).map( 16 | offset => Ax.create() 17 | .skewT(offset * 0.1) 18 | .translate(Parameter.point( 19 | Parameter.sin(Parameter.t()).mapValue(x => 45 * (x + 1)), 20 | Parameter.cos(Parameter.t()).mapValue(x => 45 * (x + 1)) 21 | ) 22 | ) 23 | .fillStyle("white").fillRect([-1, -1], [3,3]) 24 | ) 25 | ) 26 | ); 27 | 28 | helper.playExample("@name", 20, animator, 100, 100); 29 | 30 | -------------------------------------------------------------------------------- /dist/src/utils.js: -------------------------------------------------------------------------------- 1 | function arrayInitialize(n, factory) { 2 | var arr = new Array(n); 3 | for (var i = 0; i < n; i++) 4 | arr[i] = factory(); 5 | return arr; 6 | } 7 | exports.arrayInitialize = arrayInitialize; 8 | function isFunction(x) { 9 | return typeof x == 'function'; 10 | } 11 | exports.isFunction = isFunction; 12 | 13 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy91dGlscy50cyJdLCJuYW1lcyI6WyJhcnJheUluaXRpYWxpemUiLCJpc0Z1bmN0aW9uIl0sIm1hcHBpbmdzIjoiQUFBQSx5QkFBbUMsQ0FBUyxFQUFFLE9BQWdCO0lBQzdEQSxJQUFJQSxHQUFHQSxHQUFHQSxJQUFJQSxLQUFLQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQTtJQUN2QkEsR0FBR0EsQ0FBQ0EsQ0FBQ0EsR0FBR0EsQ0FBQ0EsQ0FBQ0EsR0FBR0EsQ0FBQ0EsRUFBRUEsQ0FBQ0EsR0FBR0EsQ0FBQ0EsRUFBRUEsQ0FBQ0EsRUFBRUE7UUFBRUEsR0FBR0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsR0FBR0EsT0FBT0EsRUFBRUEsQ0FBQ0E7SUFDL0NBLE1BQU1BLENBQUNBLEdBQUdBLENBQUNBO0FBQ1pBLENBQUNBO0FBSmUsdUJBQWUsa0JBSTlCLENBQUE7QUFFRCxvQkFBMkIsQ0FBTTtJQUNoQ0MsTUFBTUEsQ0FBQ0EsT0FBT0EsQ0FBQ0EsSUFBSUEsVUFBVUEsQ0FBQ0E7QUFDL0JBLENBQUNBO0FBRmUsa0JBQVUsYUFFekIsQ0FBQSIsImZpbGUiOiJzcmMvdXRpbHMuanMiLCJzb3VyY2VzQ29udGVudCI6W251bGxdLCJzb3VyY2VSb290IjoiL3NvdXJjZS8ifQ== 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Tom Larkworthy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /html/scrap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe - scrap 5 | 6 | 7 | 8 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /html/parametric.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe - parametric 5 | 6 | 7 | 8 | 9 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /dist/examples/rainbow_sines.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(); 5 | function foreverDot(size, css_color) { 6 | return Ax.create().fillStyle(css_color).fillRect([-size / 2, -size / 2], [size, size]); 7 | } 8 | var WIDTH = 100; 9 | var HEIGHT = 100; 10 | var SINS = 3; 11 | //each frame, first draw black background to erase the previous contents 12 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 13 | animator.play(Ax.create().parallel(Ax.range(0, WIDTH).map(function (x) { 14 | // for each index we create a 10 sinwaves 15 | return Ax.create().parallel(Ax.range(0, SINS).map(function (i) { 16 | return Ax.create() 17 | .translate(Parameter.point(x, Parameter 18 | .sin(Parameter.t().mapValue(function (t) { return Math.sin(t + i * 4 + x / WIDTH) * 10 + t / 2 + x / WIDTH * Math.PI + i / SINS * Math.PI * 2; })) 19 | .mapValue(function (s) { return HEIGHT * (0.45 * s + 0.5); }))) 20 | .pipe(foreverDot(3, Parameter.rgba(255, Parameter 21 | .sin(Parameter.t().mapValue(function (t) { return x / WIDTH + t * 2 + i; })) 22 | .mapValue(function (s) { return s * 125 + 125; }), 0, 1))); 23 | })); 24 | }))); 25 | helper.playExample("@name", 32, animator, WIDTH, 100); 26 | -------------------------------------------------------------------------------- /dist/examples/example3.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(); 5 | // fixed base color for particles 6 | var red = 255, green = 50, blue = 50; 7 | // alpha fades out to make the particles evaporate over time 8 | var alpha = Parameter.t().mapValue(function (t) { return 0.1 / (t * 5 + 0.1); }); 9 | // our base particle is of variable size and color 10 | function permDot(size, css_color) { 11 | return Ax.create().fillStyle(css_color).fillRect([-size / 2, -size / 2], [size, size]); 12 | } 13 | // Reset seed once via sideeffect (I feel this shouldn't typecheck :/) 14 | animator.play(Parameter.seedrnd("seed").take(1)); 15 | // each frame, first draw black background to erase the previous contents 16 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 17 | // a ring of exploding particles that fade our 18 | animator.play(Ax.create() 19 | .globalCompositeOperation("lighter") // use additive blending 20 | .clone(500, Ax.create() // clone 500 particles 21 | .translate([50, 50]) // move to center of canvas 22 | .velocity(Parameter.first(Parameter.rndNormal(50))) // choose a random direction 23 | .parallel([ 24 | permDot(1, Parameter.rgba(red, green, blue, alpha)), 25 | permDot(5, Parameter.rgba(red, green, blue, alpha)) // with a dimmer surround 26 | ]))); 27 | helper.playExample("@name", 15, animator, 100, 100); 28 | -------------------------------------------------------------------------------- /examples/elliptical.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as Rx from "rx"; 3 | import * as Ax from "../src/animaxe"; 4 | import * as helper from "../src/helper"; 5 | import * as events from "../src/events"; 6 | import * as Parameter from "../src/Parameter"; 7 | import * as svg from "../src/svg"; 8 | 9 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 10 | 11 | var cases = [ 12 | /* 13 | 'M10 20 A15 15 0 0 0 30 20', // checked against SVG behaviour 14 | 'M40 20 A15 15 0 0 1 60 20', // checked against SVG behaviour 15 | 'M70 20 A15 15 0 1 0 90 20', // checked against SVG behaviour 16 | 'M10 50 A15 15 0 1 1 30 50', // checked against SVG behaviour 17 | 18 | 'M50 40 A15 15 0 0 0 50 60', // checked against SVG behaviour 19 | 'M80 40 A15 15 0 0 1 80 60', // checked against SVG behaviour 20 | 'M20 70 A15 15 0 1 0 20 90', // checked against SVG behaviour 21 | 'M50 70 A15 15 0 1 1 50 90', // checked against SVG behaviour 22 | */ 23 | 'M80 70 A5 5 0 1 1 80 90', 24 | ]; 25 | 26 | for (var t = 0; t < Math.PI * 2 ; t += 0.1) { 27 | var x = Math.sin(t) * 15 + 50; 28 | var y = Math.cos(t) * 15 + 50; 29 | 30 | // cases.push('M50 50 A5 5 0 1 1 ' + x + ' ' + y); 31 | } 32 | for (var i = 0; i < cases.length; i++) { 33 | animator.play( 34 | svg.svgpath( 35 | Ax.create().beginPath().strokeStyle("red"), 36 | cases[i] 37 | ).stroke() 38 | ); 39 | } 40 | 41 | 42 | helper.playExample("@name", 1, animator, 100, 100); 43 | -------------------------------------------------------------------------------- /examples/svg.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | import * as svg from "../src/svg"; 7 | 8 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 9 | 10 | //each frame, first draw black background to erase the previous contents 11 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 12 | 13 | animator.play(svg.svgpath(Ax.create().beginPath().strokeStyle("blue"), 14 | 'M3,7 5-6 L1,7 1e2-.4 m-10,10 l10,0 ' + 15 | 'V27 89 H23 v10 h10 ' + 16 | 'C33,43 38,47 43,47 c0,5 5,10 10,10 ' + 17 | // 'S63,67 63,67 s-10,10 10,10 ' + // smooth curve to 18 | // 'Q50,50 73,57 q20,-5 0,-10 ' + // NaN quadratic curve :/ 19 | // 'T70,40 t0,-15 ' + // smooth quadratic curve to 20 | 'A5,5 45 1,0 40,20 a5,5 20 0,1 -10-10 z' 21 | ).stroke() 22 | ); 23 | 24 | 25 | 26 | // Using http://anthonydugois.com/svg-path-builder/ 27 | 28 | animator.play(svg.svgpath(Ax.create().beginPath().strokeStyle("yellow").lineWidth(20).scale([0.1, 0.1]), 29 | 'M350 300 a50 50 0 1 0 -200 0 c0 100 200 0 200 100 a50 50 0 1 1 -200 0 ' + 30 | 'M400 250 V%1 L500 500 L600 400 V250 ' + 31 | 'M850 300 A50 50 0 1 0 650 300 V400 A50 50 0 1 0 850 400 V350 H750', Parameter.cos(Parameter.t()).mapValue(x => x * 400) 32 | ).stroke() 33 | ); 34 | 35 | 36 | helper.playExample("@name", 10, animator, 100, 100); 37 | -------------------------------------------------------------------------------- /html/lissajous_color.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - lissajous_color 5 | 6 | 7 | 8 | 9 | 10 | 11 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "animaxe", 3 | "version": "0.0.7", 4 | "description": "A vector drawing library across time. A functional reactive approach to animation.", 5 | "author": "Tom Larkworthy", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "git@github.com:tomlarkworthy/animaxe.git" 10 | }, 11 | "scripts": {}, 12 | "dependencies": { 13 | "ctx-get-transform": "^1.0.3", 14 | "gl-mat3": "^1.0.0", 15 | "husl": "^6.0.1", 16 | "rx": "^4.0.7", 17 | "svg-path-parser": "^1.0.1" 18 | }, 19 | "devDependencies": { 20 | "async": "0.7.0", 21 | "chai": "^3.2.0", 22 | "del": "^2.0.2", 23 | "expect": "^1.9.0", 24 | "file-compare": "0.0.2", 25 | "gifencoder": "^1.0.6", 26 | "gulp": "^3.9.0", 27 | "gulp-livereload": "^3.8.1", 28 | "gulp-mocha": "^2.1.3", 29 | "gulp-peg": "^0.2.0", 30 | "gulp-rename": "^1.2.2", 31 | "gulp-replace": "^0.5.4", 32 | "gulp-sourcemaps": "^1.5.2", 33 | "gulp-template": "^3.0.0", 34 | "gulp-tslint": "^3.3.0", 35 | "gulp-typedoc": "^1.2.1", 36 | "gulp-typescript": "^2.8.1", 37 | "gulp-util": "^3.0.6", 38 | "merge2": "^0.3.6", 39 | "pegjs": "^0.7.0", 40 | "seedrandom": "^2.4.2", 41 | "should": "^7.1.0", 42 | "source-map-support": "^0.3.3", 43 | "systemjs": "^0.19.6", 44 | "through2": "^2.0.0", 45 | "ts-loader": "^0.5.6", 46 | "typescript": "1.8.0-dev.20151124", 47 | "vinyl-transform": "^1.0.0", 48 | "webpack": "^1.12.2" 49 | }, 50 | "optionalDependencies": { 51 | "canvas": "1.3.5" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import {Parameter} from "./Parameter" 2 | /** 3 | * A css encoded color, e.g. "rgba(255, 125, 32, 0.5)" or "red" 4 | */ 5 | export type Color = string 6 | /** 7 | * A 2D array of numbers used for representing points or vectors 8 | */ 9 | export type Point = [number, number] 10 | 11 | /** 12 | * A literal or a dynamic Parameter alias, used as arguments to animations. 13 | */ 14 | export type NumberArg = number | Parameter 15 | /** 16 | * A literal or a dynamic Parameter alias, used as arguments to animations. 17 | */ 18 | export type PointArg = Point | Parameter 19 | /** 20 | * A literal or a dynamic Parameter alias, used as arguments to animations. 21 | */ 22 | export type ColorArg = Color | Parameter 23 | /** 24 | * A literal or a dynamic Parameter alias, used as arguments to animations. 25 | */ 26 | export type StringArg = string | Parameter 27 | /** 28 | * A literal or a dynamic Parameter alias, used as arguments to animations. 29 | */ 30 | export type BooleanArg = boolean | Parameter 31 | 32 | 33 | export function applyMixins(derivedCtor: any, baseCtors: any[]) { 34 | baseCtors.forEach(baseCtor => { 35 | Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { 36 | derivedCtor.prototype[name] = baseCtor.prototype[name]; 37 | }) 38 | }); 39 | } 40 | 41 | export function assert(predicate: boolean, msg : string = "Assertion error") { 42 | if (!predicate) { 43 | throw new Error(msg); 44 | } 45 | } 46 | 47 | export function stackTrace() { 48 | var err = new Error(); 49 | return (err).stack; 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /html/svg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - svg 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/skew.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - skew 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/arrows.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - arrows 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example3 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example4 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example5 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example6 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - example7 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/lissajous.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - lissajous 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/elliptical.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - elliptical 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/example.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - <%= name %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/readme_order.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - readme_order 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/rainbow_sines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animaxe Example - rainbow_sines 5 | 6 | 7 | 8 | 9 | 10 | 11 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /dist/examples/readme_order.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An Illistration of the order of operations of 3 | Ax.create() // begin an new animation tree 4 | .strokeStyle("green") // top of animation tree the style is set to green 5 | .parrallel([ 6 | Ax.create().stroke() // stroke green, downstream of parrallel 7 | Ax.create().strokeStyle("red").stroke(), //stroke red 8 | Ax.create().stroke() // stroke green, not affected by red sibling 9 | ]) 10 | .stroke() // stroke green, downstream of parrallel which is downstream of top 11 | ]) 12 | */ 13 | var Ax = require("../src/animaxe"); 14 | var helper = require("../src/helper"); 15 | var Parameter = require("../src/Parameter"); 16 | var animator = helper.getExampleAnimator(); 17 | function flowNode(pos, label, id, active) { 18 | return Ax.create() 19 | .translate(pos) 20 | .fillText(label, [0, 0]); 21 | } 22 | //each frame, first draw black background to erase the previous contents 23 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 24 | var timeline = Parameter.constant(1).take(1).then(Parameter.constant(1).take(2)); 25 | // move the drawing context frame of reference to the center (50,50) and then move it by a +ve x velocity, 26 | // so the frame of reference moves over time. 27 | // then draw our 2 frame spark animation in a loop so it draws forever 28 | animator.play(Ax.create().fillStyle("white") 29 | .parallel([ 30 | flowNode([10, 10], "Ax.create()", 0, timeline), 31 | flowNode([20, 20], "strokeStyle(\"green\")", 0, timeline), 32 | flowNode([30, 30], "parrallel([", 0, timeline), 33 | ])); 34 | // the helper function pipes injects the context, either from a web canvas or a fake node.js one. 35 | helper.playExample("@name", 20, animator, 100, 100); 36 | -------------------------------------------------------------------------------- /dist/examples/example4.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(100, 100); 5 | var red = 255; 6 | var green = 50; 7 | var blue = 50; 8 | function foreverDot(size, css_color) { 9 | return Ax.create().fillStyle(css_color).fillRect([-size / 2, -size / 2], [size, size]); 10 | } 11 | var bigSin = Parameter.sin(Parameter.t().mapValue(function (t) { return t * Math.PI; })).mapValue(function (x) { return Math.round(x * 40 + 50); }); 12 | var bigCos = Parameter.cos(Parameter.t().mapValue(function (t) { return t * Math.PI; })).mapValue(function (x) { return Math.round(x * 40 + 50); }); 13 | var fastCos = Parameter.cos(Parameter.t().mapValue(function (t) { return 2 * t * Math.PI; })).mapValue(function (x) { return Math.round(x * 38 + 50); }); 14 | var fastSin = Parameter.sin(Parameter.t().mapValue(function (t) { return 2 * t * Math.PI; })).mapValue(function (x) { return Math.round(x * 38 + 50); }); 15 | //each frame, first draw black background to erase the previous contents 16 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 17 | // we draw single pixels of different hues moving on a circle circumference 18 | animator.play(Ax.create().parallel([ 19 | Ax.create().translate(Parameter.point(fastCos, fastSin)).pipe(foreverDot(1, Parameter.hsl(120, 20, 10))), 20 | Ax.create().translate(Parameter.point(bigCos, bigSin)).pipe(foreverDot(1, Parameter.hsl(240, 20, 30))), 21 | Ax.create().translate(Parameter.point(bigSin, bigCos)).pipe(foreverDot(1, Parameter.hsl(60, 20, 25))) 22 | ])); 23 | // we apply a glow filter last 24 | animator.play(Ax.create().glow(0.01)); 25 | helper.playExample("@name", 20, animator, 100, 100); 26 | -------------------------------------------------------------------------------- /examples/example4.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 8 | 9 | var red = 255; 10 | var green = 50; 11 | var blue = 50; 12 | 13 | function foreverDot(size: number, css_color: Ax.ColorArg): Ax.Operation { 14 | return Ax.create().fillStyle(css_color).fillRect([-size/2, -size/2], [size, size]); 15 | } 16 | 17 | var bigSin = Parameter.sin(Parameter.t().mapValue(t => t * Math.PI)).mapValue(x => Math.round(x * 40 + 50)); 18 | var bigCos = Parameter.cos(Parameter.t().mapValue(t => t * Math.PI)).mapValue(x => Math.round(x * 40 + 50)); 19 | var fastCos = Parameter.cos(Parameter.t().mapValue(t => 2 * t * Math.PI)).mapValue(x => Math.round(x * 38 + 50)); 20 | var fastSin = Parameter.sin(Parameter.t().mapValue(t => 2 * t * Math.PI)).mapValue(x => Math.round(x * 38 + 50)); 21 | 22 | //each frame, first draw black background to erase the previous contents 23 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 24 | 25 | // we draw single pixels of different hues moving on a circle circumference 26 | animator.play( 27 | Ax.create().parallel([ 28 | Ax.create().translate(Parameter.point(fastCos, fastSin)).pipe(foreverDot(1, Parameter.hsl(120, 20, 10))), 29 | Ax.create().translate(Parameter.point(bigCos, bigSin)) .pipe(foreverDot(1, Parameter.hsl(240, 20, 30))), 30 | Ax.create().translate(Parameter.point(bigSin, bigCos)) .pipe(foreverDot(1, Parameter.hsl(60, 20, 25))) 31 | ]) 32 | ); 33 | 34 | // we apply a glow filter last 35 | animator.play(Ax.create().glow(0.01)); 36 | 37 | helper.playExample("@name", 20, animator, 100, 100); 38 | 39 | -------------------------------------------------------------------------------- /dist/src/types.js: -------------------------------------------------------------------------------- 1 | function applyMixins(derivedCtor, baseCtors) { 2 | baseCtors.forEach(function (baseCtor) { 3 | Object.getOwnPropertyNames(baseCtor.prototype).forEach(function (name) { 4 | derivedCtor.prototype[name] = baseCtor.prototype[name]; 5 | }); 6 | }); 7 | } 8 | exports.applyMixins = applyMixins; 9 | function assert(predicate, msg) { 10 | if (msg === void 0) { msg = "Assertion error"; } 11 | if (!predicate) { 12 | throw new Error(msg); 13 | } 14 | } 15 | exports.assert = assert; 16 | function stackTrace() { 17 | var err = new Error(); 18 | return err.stack; 19 | } 20 | exports.stackTrace = stackTrace; 21 | 22 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy90eXBlcy50cyJdLCJuYW1lcyI6WyJhcHBseU1peGlucyIsImFzc2VydCIsInN0YWNrVHJhY2UiXSwibWFwcGluZ3MiOiJBQWdDQSxxQkFBNEIsV0FBZ0IsRUFBRSxTQUFnQjtJQUMxREEsU0FBU0EsQ0FBQ0EsT0FBT0EsQ0FBQ0EsVUFBQUEsUUFBUUE7UUFDdEJBLE1BQU1BLENBQUNBLG1CQUFtQkEsQ0FBQ0EsUUFBUUEsQ0FBQ0EsU0FBU0EsQ0FBQ0EsQ0FBQ0EsT0FBT0EsQ0FBQ0EsVUFBQUEsSUFBSUE7WUFDdkRBLFdBQVdBLENBQUNBLFNBQVNBLENBQUNBLElBQUlBLENBQUNBLEdBQUdBLFFBQVFBLENBQUNBLFNBQVNBLENBQUNBLElBQUlBLENBQUNBLENBQUNBO1FBQzNEQSxDQUFDQSxDQUFDQSxDQUFBQTtJQUNOQSxDQUFDQSxDQUFDQSxDQUFDQTtBQUNQQSxDQUFDQTtBQU5lLG1CQUFXLGNBTTFCLENBQUE7QUFFRCxnQkFBdUIsU0FBa0IsRUFBRSxHQUFnQztJQUFoQ0MsbUJBQWdDQSxHQUFoQ0EsdUJBQWdDQTtJQUN2RUEsRUFBRUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsU0FBU0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7UUFDYkEsTUFBTUEsSUFBSUEsS0FBS0EsQ0FBQ0EsR0FBR0EsQ0FBQ0EsQ0FBQ0E7SUFDekJBLENBQUNBO0FBQ0xBLENBQUNBO0FBSmUsY0FBTSxTQUlyQixDQUFBO0FBRUQ7SUFDSUMsSUFBSUEsR0FBR0EsR0FBR0EsSUFBSUEsS0FBS0EsRUFBRUEsQ0FBQ0E7SUFDdEJBLE1BQU1BLENBQU9BLEdBQUlBLENBQUNBLEtBQUtBLENBQUNBO0FBQzVCQSxDQUFDQTtBQUhlLGtCQUFVLGFBR3pCLENBQUEiLCJmaWxlIjoic3JjL3R5cGVzLmpzIiwic291cmNlc0NvbnRlbnQiOltudWxsXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= 23 | -------------------------------------------------------------------------------- /types/seedrandom.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for seedrandom 2.4.2 2 | // Project: https://github.com/davidbau/seedrandom 3 | // Definitions by: Kern Handa 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | declare type seedrandomStateType = boolean | (() => prng); 7 | 8 | interface prng { 9 | new(seed?: string, options?: seedRandomOptions, callback?: any): prng; 10 | (): number; 11 | quick(): number; 12 | int32(): number; 13 | double(): number; 14 | state(): () => prng; 15 | } 16 | 17 | interface seedrandom_prng { 18 | (seed?: string, options?: seedRandomOptions, callback?: any): prng; 19 | alea: (seed?: string, options?: seedRandomOptions, callback?: seedrandomCallback) => prng; 20 | xor128: (seed?: string, options?: seedRandomOptions, callback?: seedrandomCallback) => prng; 21 | tychei: (seed?: string, options?: seedRandomOptions, callback?: seedrandomCallback) => prng; 22 | xorwow: (seed?: string, options?: seedRandomOptions, callback?: seedrandomCallback) => prng; 23 | xor4096: (seed?: string, options?: seedRandomOptions, callback?: seedrandomCallback) => prng; 24 | xorshift7: (seed?: string, options?: seedRandomOptions, callback?: seedrandomCallback) => prng; 25 | quick: (seed?: string, options?: seedRandomOptions, callback?: seedrandomCallback) => prng; 26 | } 27 | 28 | interface seedrandomCallback { 29 | (prng?: prng, shortseed?: string, global?: boolean, state?: seedrandomStateType): prng; 30 | } 31 | 32 | interface seedRandomOptions { 33 | entropy?: boolean; 34 | 'global'?: boolean; 35 | state?: seedrandomStateType; 36 | pass?: seedrandomCallback; 37 | } 38 | 39 | interface Math { 40 | seedrandom(seed?:string) 41 | } 42 | 43 | declare var seedrandom: seedrandom_prng; 44 | 45 | declare module "seedrandom" { 46 | export = seedrandom; 47 | } -------------------------------------------------------------------------------- /examples/example3.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(); 8 | 9 | // fixed base color for particles 10 | var red = 255, green = 50, blue = 50; 11 | // alpha fades out to make the particles evaporate over time 12 | var alpha = Parameter.t().mapValue(t => 0.1 / (t*5 + 0.1)); 13 | 14 | // our base particle is of variable size and color 15 | function permDot(size: number, css_color: Ax.ColorArg): Ax.Operation { 16 | return Ax.create().fillStyle(css_color).fillRect([-size/2, -size/2], [size, size]); 17 | } 18 | 19 | // Reset seed once via sideeffect (I feel this shouldn't typecheck :/) 20 | animator.play(Parameter.seedrnd("seed").take(1)); 21 | 22 | // each frame, first draw black background to erase the previous contents 23 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 24 | // a ring of exploding particles that fade our 25 | animator.play(Ax.create() 26 | .globalCompositeOperation("lighter") // use additive blending 27 | .clone(500, Ax.create() // clone 500 particles 28 | .translate([50, 50]) // move to center of canvas 29 | .velocity(Parameter.first(Parameter.rndNormal(50))) // choose a random direction 30 | .parallel([ // draw overlapping particles 31 | permDot(1, Parameter.rgba(red, green, blue, alpha)), // so the center is brighter 32 | permDot(5, Parameter.rgba(red, green, blue, alpha)) // with a dimmer surround 33 | ]) 34 | ) 35 | ); 36 | 37 | helper.playExample("@name", 15, animator, 100, 100); 38 | 39 | -------------------------------------------------------------------------------- /dist/examples/example2.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(); 5 | //a line between two points of a specified thickness and color (which are temporally varying parameters) 6 | function thickLine1tick(thickness, start, end, css_color) { 7 | return Ax.create() 8 | .take(1) 9 | .strokeStyle(css_color) 10 | .withinPath(Ax.create() 11 | .lineWidth(thickness) 12 | .moveTo(start) 13 | .lineTo(end)) 14 | .stroke(); 15 | } 16 | /** 17 | * Three frame animation of a thinning line. Animations are displaced in time so even if the start and end streams move, 18 | * the line doesn't 19 | */ 20 | function sparkLine(start, end, css_color) { 21 | return thickLine1tick(6, //thick line 22 | start, end, css_color) 23 | .then(thickLine1tick(2, //medium line 24 | Parameter.skewT(-0.1, start), Parameter.skewT(-0.1, end), css_color)) 25 | .then(thickLine1tick(1, //thin line 26 | Parameter.skewT(-0.2, start), Parameter.skewT(-0.2, end), css_color)); 27 | } 28 | //large circle funcitons 29 | var bigSin = Parameter.sin(Parameter.t().mapValue(function (x) { return Math.PI * x * 2; })).mapValue(function (x) { return x * 40 + 50; }); 30 | var bigCos = Parameter.cos(Parameter.t().mapValue(function (x) { return Math.PI * x * 2; })).mapValue(function (x) { return x * 40 + 50; }); 31 | //periodic color 32 | var red = 255; 33 | var green = Parameter.sin(Parameter.t().mapValue(function (x) { return x * 2; })).mapValue(function (x) { return x * 100 + 55; }); 34 | var blue = 50; 35 | //each frame, first draw black background to erase the previous contents 36 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 37 | animator.play(Ax.create().emit(sparkLine(Parameter.point(bigSin, bigCos), Parameter.skewT(-0.1, Parameter.point(bigSin, bigCos)), Parameter.rgba(red, green, blue, 1)))); 38 | helper.playExample("@name", 20, animator, 100, 100); 39 | -------------------------------------------------------------------------------- /examples/rainbow_sines.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(); 8 | 9 | function foreverDot(size: number, css_color: Ax.ColorArg): Ax.Operation { 10 | return Ax.create().fillStyle(css_color).fillRect([-size/2, -size/2], [size, size]); 11 | } 12 | 13 | 14 | var WIDTH = 100; 15 | var HEIGHT = 100; 16 | var SINS = 3; 17 | 18 | //each frame, first draw black background to erase the previous contents 19 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 20 | 21 | 22 | animator.play( 23 | Ax.create().parallel( 24 | Ax.range(0, WIDTH).map(x => { 25 | // for each index we create a 10 sinwaves 26 | return Ax.create().parallel( 27 | Ax.range(0, SINS).map(i => { 28 | return Ax.create() 29 | .translate( 30 | Parameter.point( 31 | x, 32 | Parameter 33 | .sin( 34 | Parameter.t().mapValue(t => Math.sin(t + i * 4 + x/ WIDTH) * 10 + t / 2 + x / WIDTH * Math.PI + i / SINS * Math.PI * 2) 35 | ) 36 | .mapValue(s => HEIGHT * (0.45 * s + 0.5)) 37 | 38 | ) 39 | ) 40 | .pipe(foreverDot(3, Parameter.rgba(255, 41 | Parameter 42 | .sin( 43 | Parameter.t().mapValue(t => x / WIDTH + t * 2 + i) 44 | ) 45 | .mapValue(s => s * 125 + 125), 46 | 0,1))); 47 | }) 48 | ); 49 | }) 50 | ) 51 | ); 52 | 53 | 54 | helper.playExample("@name", 32, animator, WIDTH, 100); -------------------------------------------------------------------------------- /examples/readme_order.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An Illistration of the order of operations of 3 | Ax.create() // begin an new animation tree 4 | .strokeStyle("green") // top of animation tree the style is set to green 5 | .parrallel([ 6 | Ax.create().stroke() // stroke green, downstream of parrallel 7 | Ax.create().strokeStyle("red").stroke(), //stroke red 8 | Ax.create().stroke() // stroke green, not affected by red sibling 9 | ]) 10 | .stroke() // stroke green, downstream of parrallel which is downstream of top 11 | ]) 12 | */ 13 | 14 | 15 | import * as Rx from "rx"; 16 | import * as Ax from "../src/animaxe"; 17 | import * as helper from "../src/helper"; 18 | import * as events from "../src/events"; 19 | import * as Parameter from "../src/Parameter"; 20 | 21 | var animator: Ax.Animator = helper.getExampleAnimator(); 22 | 23 | function flowNode( 24 | pos: Ax.Point, 25 | label: string, 26 | id: number, 27 | active: Parameter.Parameter): Ax.Operation { //we could be clever and let spark take a seq, but user functions should be simple 28 | return Ax.create() 29 | .translate(pos) 30 | .fillText(label, [0,0]) 31 | 32 | } 33 | //each frame, first draw black background to erase the previous contents 34 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 35 | 36 | var timeline = 37 | Parameter.constant(1).take(1).then( 38 | Parameter.constant(1).take(2) 39 | ) 40 | 41 | // move the drawing context frame of reference to the center (50,50) and then move it by a +ve x velocity, 42 | // so the frame of reference moves over time. 43 | // then draw our 2 frame spark animation in a loop so it draws forever 44 | animator.play( 45 | Ax.create().fillStyle("white") 46 | .parallel([ 47 | flowNode([10, 10], "Ax.create()", 0, timeline), 48 | flowNode([20, 20], "strokeStyle(\"green\")", 0, timeline), 49 | flowNode([30, 30], "parrallel([", 0, timeline), 50 | ]) 51 | ); 52 | 53 | // the helper function pipes injects the context, either from a web canvas or a fake node.js one. 54 | helper.playExample("@name", 20, animator, 100, 100); 55 | 56 | -------------------------------------------------------------------------------- /examples/example1.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(); 8 | 9 | //2 frame animated glow 10 | function spark(color: Ax.ColorArg): Ax.Operation { //we could be clever and let spark take a seq, but user functions should be simple 11 | return Ax.create() 12 | .take(1) 13 | .fillStyle(color) 14 | .fillRect([-2, -2], [5,5]) 15 | .then(Ax.create() 16 | .take(1) 17 | .fillStyle(color) 18 | .fillRect([-1, -1], [3,3]) 19 | ); 20 | } 21 | //large circle funcitons 22 | var bigSin = Parameter.sin(Parameter.t().mapValue(x=>x * Math.PI * 2)).mapValue(x => x * 40 + 50); 23 | var bigCos = Parameter.cos(Parameter.t().mapValue(x=>x * Math.PI * 2)).mapValue(x => x * 40 + 50); 24 | 25 | var red = Parameter.sin(Parameter.t().mapValue(x=>x * Math.PI)).mapValue(x => x * 125 + 125); 26 | var green = Parameter.sin(Parameter.t().mapValue(x=>x * Math.PI)).mapValue(x => x * 55 + 200); 27 | 28 | //each frame, first draw black background to erase the previous contents 29 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 30 | 31 | 32 | // move the drawing context frame of reference to the center (50,50) and then move it by a +ve x velocity, 33 | // so the frame of reference moves over time. 34 | // then draw our 2 frame spark animation in a loop so it draws forever 35 | animator.play(Ax.create() 36 | .translate([50,50]) 37 | .velocity([50,0]) 38 | .loop( 39 | spark("#FFFFFF") 40 | ) 41 | ); 42 | 43 | // move the draw context to a coordinate determined by trig (i.e. in a circle) 44 | animator.play(Ax.create() 45 | .loop(Ax.create() 46 | .translate(Parameter.point(bigSin, bigCos)) 47 | .pipe( 48 | spark(Parameter.rgba(red, green, 0, 1)) 49 | ) 50 | ) 51 | ); 52 | 53 | // tween between the center (50,50) and a point on a circle. This has the effect of moving the inner spark animation 54 | // in a archimedes spiral. 55 | animator.play(Ax.create() 56 | .tween_linear([50,50], Parameter.point(bigSin, bigCos), 1) 57 | .loop( 58 | spark("red") 59 | ) 60 | ); 61 | // the helper function pipes injects the context, either from a web canvas or a fake node.js one. 62 | helper.playExample("@name", 20, animator, 100, 100); 63 | -------------------------------------------------------------------------------- /examples/example2.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(); 8 | 9 | //a line between two points of a specified thickness and color (which are temporally varying parameters) 10 | function thickLine1tick( 11 | thickness: number, 12 | start: Ax.PointArg, 13 | end: Ax.PointArg, 14 | css_color: string | Ax.ColorArg) 15 | : Ax.Operation { 16 | return Ax.create() 17 | .take(1) 18 | .strokeStyle(css_color) 19 | .withinPath(Ax.create() 20 | .lineWidth(thickness) 21 | .moveTo(start) 22 | .lineTo(end) 23 | ) 24 | .stroke(); 25 | } 26 | 27 | /** 28 | * Three frame animation of a thinning line. Animations are displaced in time so even if the start and end streams move, 29 | * the line doesn't 30 | */ 31 | function sparkLine(start: Ax.PointArg, end: Ax.PointArg, css_color: Ax.ColorArg): Ax.Operation { //we could be clever and let spark take a seq, but user functions should be simple 32 | return thickLine1tick(6, //thick line 33 | start, 34 | end, css_color) 35 | .then(thickLine1tick(2, //medium line 36 | Parameter.skewT(-0.1, start), 37 | Parameter.skewT(-0.1, end), 38 | css_color)) 39 | .then(thickLine1tick(1, //thin line 40 | Parameter.skewT(-0.2, start), 41 | Parameter.skewT(-0.2, end), 42 | css_color)); 43 | } 44 | 45 | //large circle funcitons 46 | var bigSin = Parameter.sin(Parameter.t().mapValue(x => Math.PI * x * 2)).mapValue(x => x * 40 + 50); 47 | var bigCos = Parameter.cos(Parameter.t().mapValue(x => Math.PI * x * 2)).mapValue(x => x * 40 + 50); 48 | 49 | //periodic color 50 | var red = 255; 51 | var green = Parameter.sin(Parameter.t().mapValue(x => x*2)).mapValue(x => x * 100 + 55); 52 | var blue = 50; 53 | 54 | //each frame, first draw black background to erase the previous contents 55 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 56 | 57 | animator.play( 58 | Ax.create().emit( 59 | sparkLine( 60 | Parameter.point(bigSin,bigCos), 61 | Parameter.skewT(-0.1, Parameter.point(bigSin,bigCos)), 62 | Parameter.rgba(red,green,blue,1) 63 | ) 64 | ) 65 | ); 66 | 67 | 68 | helper.playExample("@name", 20, animator, 100, 100); -------------------------------------------------------------------------------- /dist/examples/parametric.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(100, 100); 5 | //each frame, first draw black background to erase the previous contents 6 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 7 | // we draw single pixels of different hues moving on a circle circumference 8 | /* 9 | export function trace( 10 | equations: Parameter<((t: number) => number)[]>, 11 | t_min: types.NumberArg, 12 | t_max: types.NumberArg, 13 | tolerance_px2: types.NumberArg = 1, 14 | minimum_splits: types.NumberArg = 0): Parameter<{point: number[], t: number}[]> { 15 | return equations.combine( 16 | () => (equations: ((t: number) => number)[], t_min: number, t_max: number, tolerance_px2: number, minimum_splits: number) => { 17 | return parametric.trace(equations, t_min, t_max, tolerance_px2, minimum_splits); 18 | }, 19 | from(t_min), 20 | from(t_max), 21 | from(tolerance_px2), 22 | from(minimum_splits) 23 | ) 24 | } 25 | */ 26 | /** 27 | * https://en.wikipedia.org/wiki/Lissajous_curve 28 | * Paramteric equations of x = Asin(at + i), y = Bsin(bt) 29 | */ 30 | function lissajous(A, a, B, b, i) { 31 | return Parameter.from(A).combine(function () { return function (A, a, B, b, i) { 32 | return [function (t) { return A * Math.sin(a * t + i); }, function (t) { return B * Math.sin(b * t); }]; 33 | }; }, Parameter.from(A), Parameter.from(a), Parameter.from(B), Parameter.from(b), Parameter.from(i)); 34 | } 35 | // t => point[] 36 | // mapped to 37 | // t => mutation[] 38 | // t => Canvas 39 | /* 40 | animator.play( 41 | Ax.create() 42 | .beginPath() 43 | .pipe( 44 | Ax.create().reduce( 45 | Parameter.trace( 46 | lissajous(45, 1, 45, 2, 0), 47 | Parameter.t(), Parameter.t().mapValue(t => t + Math.PI * 2), 48 | /* accuracy * 1, 49 | /* min. splits * 4 50 | ).mapValue(array => array.map(segment => segment.point)), // t values discarded, the result is an array of 2D points, i.e. [number, number][] 51 | (animation: Ax.Animation, point: [number, number], index: number) => 52 | index == 0 ? animation.moveTo(point): animation.lineTo(point) 53 | ) 54 | ) 55 | .strokeStyle("green") 56 | .stroke() 57 | );*/ 58 | helper.playExample("@name", 20, animator, 100, 100); 59 | -------------------------------------------------------------------------------- /dist/src/List.js: -------------------------------------------------------------------------------- 1 | var __extends = (this && this.__extends) || function (d, b) { 2 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 3 | function __() { this.constructor = d; } 4 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 5 | }; 6 | function __export(m) { 7 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 8 | } 9 | var OT = require("./frp"); 10 | __export(require("./types")); 11 | var Parameter = require("./Parameter"); 12 | var List = (function (_super) { 13 | __extends(List, _super); 14 | function List(attach) { 15 | _super.call(this, attach); 16 | this.attach = attach; 17 | } 18 | List.prototype.mapElement = function (mapFn) { 19 | return new List(this.mapValue(function (vals) { return vals.map(mapFn); }).attach); 20 | }; 21 | List.prototype.slice = function (start, end) { 22 | this.combine(function () { return function (vals, start, end) { 23 | return vals.slice(start, end); 24 | }; }, Parameter.from(start), Parameter.from(end)); 25 | }; 26 | return List; 27 | })(OT.SignalFn); 28 | exports.List = List; 29 | 30 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9MaXN0LnRzIl0sIm5hbWVzIjpbIkxpc3QiLCJMaXN0LmNvbnN0cnVjdG9yIiwiTGlzdC5tYXBFbGVtZW50IiwiTGlzdC5zbGljZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQSxJQUFZLEVBQUUsV0FBTSxPQUNwQixDQUFDLENBRDBCO0FBRTNCLGlCQUFjLFNBQ2QsQ0FBQyxFQURzQjtBQUN2QixJQUFZLFNBQVMsV0FBTSxhQUUzQixDQUFDLENBRnVDO0FBSXhDO0lBQTZCQSx3QkFBc0JBO0lBQy9DQSxjQUFtQkEsTUFBNkRBO1FBQzVFQyxrQkFBTUEsTUFBTUEsQ0FBQ0EsQ0FBQ0E7UUFEQ0EsV0FBTUEsR0FBTkEsTUFBTUEsQ0FBdURBO0lBRWhGQSxDQUFDQTtJQUVERCx5QkFBVUEsR0FBVkEsVUFBZ0JBLEtBQWlCQTtRQUM3QkUsTUFBTUEsQ0FBQ0EsSUFBSUEsSUFBSUEsQ0FBTUEsSUFBSUEsQ0FBQ0EsUUFBUUEsQ0FBQ0EsVUFBQ0EsSUFBU0EsSUFBS0EsT0FBQUEsSUFBSUEsQ0FBQ0EsR0FBR0EsQ0FBQ0EsS0FBS0EsQ0FBQ0EsRUFBZkEsQ0FBZUEsQ0FBQ0EsQ0FBQ0EsTUFBTUEsQ0FBQ0EsQ0FBQ0E7SUFDL0VBLENBQUNBO0lBRURGLG9CQUFLQSxHQUFMQSxVQUFNQSxLQUFzQkEsRUFBRUEsR0FBb0JBO1FBQzlDRyxJQUFJQSxDQUFDQSxPQUFPQSxDQUNSQSxjQUFNQSxPQUFBQSxVQUFDQSxJQUFTQSxFQUFFQSxLQUFhQSxFQUFFQSxHQUFXQTttQkFDeENBLElBQUlBLENBQUNBLEtBQUtBLENBQUNBLEtBQUtBLEVBQUVBLEdBQUdBLENBQUNBO1FBQXRCQSxDQUFzQkEsRUFEcEJBLENBQ29CQSxFQUMxQkEsU0FBU0EsQ0FBQ0EsSUFBSUEsQ0FBQ0EsS0FBS0EsQ0FBQ0EsRUFDckJBLFNBQVNBLENBQUNBLElBQUlBLENBQUNBLEdBQUdBLENBQUNBLENBQ3RCQSxDQUFBQTtJQUNMQSxDQUFDQTtJQUNMSCxXQUFDQTtBQUFEQSxDQWpCQSxBQWlCQ0EsRUFqQjRCLEVBQUUsQ0FBQyxRQUFRLEVBaUJ2QztBQWpCWSxZQUFJLE9BaUJoQixDQUFBIiwiZmlsZSI6InNyYy9MaXN0LmpzIiwic291cmNlc0NvbnRlbnQiOltudWxsXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= 31 | -------------------------------------------------------------------------------- /src/intersection.ts: -------------------------------------------------------------------------------- 1 | import * as frp from './frp'; 2 | import * as Rx from "rx" 3 | import * as zip from "./zip" 4 | 5 | // stream intersection &&& in AFRP 6 | export function intersection 7 | (a: frp.SignalFn, b: frp.SignalFn): frp.SignalFn { 8 | return new frp.SignalFn( 9 | (upstream: Rx.Observable) => { 10 | let a_out: Rx.Observable = a.attach(upstream); 11 | let b_out: Rx.Observable = b.attach(upstream); 12 | return zip.zip( 13 | (values: [OutA, OutB]) => extend(values[0], values[1]), 14 | a_out, b_out); 15 | } 16 | ) 17 | 18 | } 19 | 20 | function extend(first: T, second: U): T & U { 21 | let result = {}; 22 | for (let id in first) { 23 | result[id] = first[id]; 24 | } 25 | for (let id in second) { 26 | if (!result.hasOwnProperty(id)) { 27 | result[id] = second[id]; 28 | } 29 | } 30 | return result; 31 | } 32 | 33 | export function first 34 | (sf: frp.SignalFn): frp.SignalFn { 35 | return new frp.SignalFn( 36 | (upstream: Rx.Observable) => { 37 | let first: Rx.Observable = sf.attach(upstream); 38 | let second: Rx.Observable = upstream; 39 | return zip.zip( 40 | (values: [Out, Passthrough]) => extend(values[0], values[1]), 41 | first, second); 42 | } 43 | ) 44 | } 45 | 46 | export function arr(fn: (input: In) => Out): frp.SignalFn { 47 | return new frp.SignalFn( 48 | (upstream: Rx.Observable) => upstream.map(fn) 49 | ); 50 | } 51 | /// >>> operator 52 | /* 53 | export function pipe(first: frp.SignalFn, second: frp.SignalFn): frp.SignalFn { 54 | return new frp.SignalFn( 55 | (upstream: Rx.Observable) => second.attach(first.attach(upstream)) 56 | ); 57 | } 58 | 59 | /// >>loop operator 60 | export function loop(first: frp.SignalFn, second: frp.SignalFn): frp.SignalFn { 61 | return new frp.SignalFn( 62 | (upstream: Rx.Observable) => second.attach(first.attach(upstream)) 63 | ); 64 | }*/ 65 | 66 | -------------------------------------------------------------------------------- /src/parametric.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines utilities for working with parametric function, e.g. x = sin(t), y = cos(t) where t = 0 ... 10 3 | */ 4 | 5 | function evaluate(equations: ((t: number) => number)[], t: number): number[] { 6 | return equations.map(fn => fn(t)); 7 | } 8 | function midpoint(a: number[], b: number[]): number[] { 9 | return a.map((v, index) => (v + b[index]) / 2); 10 | } 11 | function add(a: number, b: number): number { 12 | return a + b; 13 | } 14 | function distance2(a: number[], b: number[]): number { 15 | return a.map((v, index) => (v - b[index]) * (v - b[index])).reduce(add, 0); 16 | } 17 | 18 | export function trace( 19 | equations: ((t: number) => number)[], 20 | t_min: number, 21 | t_max: number, 22 | tolerance_px2: number = 1, 23 | minimum_splits = 4, 24 | maximum_splits = 100, 25 | t_min_value ?: number[], 26 | t_max_value ?: number[] 27 | ): {point: number[], t: number}[] { 28 | 29 | // console.log(minimum_splits, maximum_splits); 30 | 31 | // figure out the start and end point if not provided 32 | var min = t_min_value || evaluate(equations, t_min); 33 | var max = t_max_value || evaluate(equations, t_max); 34 | 35 | var t_mid = (t_min + t_max) / 2; 36 | var mid_predict = midpoint(min, max); // guess the mid point based on linear interpolation 37 | var mid_actual = evaluate(equations, t_mid); // get the real midpoint from the equations 38 | 39 | // the distance between predicted and actuall is our error term 40 | var dist2 = distance2(mid_predict, mid_actual) 41 | 42 | var result = []; 43 | // if the caller did not specify the min_value, they will want to see it in the result set 44 | if (!t_min_value) result.push({point: min, t: t_min}); 45 | if ((dist2 < tolerance_px2 && minimum_splits <= 0) || maximum_splits <= 0) { 46 | // low error, 47 | // our lines seem to be estimating the curve ok (on the midpoint at least) 48 | } else { 49 | // high error, 50 | // we recurse by dividing the problem into 2 smaller tracing problems 51 | minimum_splits = Math.ceil((minimum_splits - 1) / 2) 52 | maximum_splits = Math.ceil((maximum_splits - 1) / 2) 53 | result = result.concat(trace(equations, t_min, t_mid, tolerance_px2, minimum_splits, maximum_splits, min, mid_actual)); 54 | result.push({point: mid_actual, t: t_mid}); 55 | result = result.concat(trace(equations, t_mid, t_max, tolerance_px2, minimum_splits, maximum_splits, mid_actual, max)); 56 | } 57 | // if the caller did not specify the max_value, they will want to see it in the result set 58 | if (!t_max_value) result.push({point: max, t: t_max}); 59 | 60 | // console.log(result); 61 | return result; 62 | } -------------------------------------------------------------------------------- /src/helper.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | import * as Rx from "rx"; 6 | import * as canvas from "./canvas"; 7 | import * as Ax from "./animaxe"; 8 | export * from "./animaxe"; 9 | import { monkeyPatchCtxToAddGetTransform as transform_fix} from "./ctx-get-transform"; 10 | 11 | export function getExampleAnimator(width: number = 100, height: number = 100): Ax.Animator { 12 | try { 13 | // In a browser environment, find a canvas 14 | var canvas:any = document.getElementById("canvas"); 15 | console.log("browser", canvas); 16 | var context: CanvasRenderingContext2D = canvas.getContext('2d'); 17 | 18 | transform_fix(context); //monkey patch context to get transform tracking 19 | 20 | var animator = new Ax.Animator(context); 21 | 22 | animator.registerEvents(canvas); 23 | return animator; 24 | } catch (err) { 25 | console.log("error, so assuming we are in node environment", err); 26 | // in a node.js environment, load a fake canvas 27 | console.log(err); 28 | var Canvas = require('canvas'); 29 | var canvas = new Canvas(width, height); 30 | console.log("node", canvas); 31 | 32 | var context: CanvasRenderingContext2D = canvas.getContext('2d'); 33 | require('ctx-get-transform')(context); //monkey patch context to get transform tracking 34 | return new Ax.Animator(context); 35 | } 36 | } 37 | 38 | export function playExample(name: string, frames: number, animator: Ax.Animator, width ?: number, height ?: number) { 39 | try { 40 | //browser 41 | var time; 42 | var render = function() { 43 | window.requestAnimationFrame(render); 44 | var now = new Date().getTime(), 45 | dt = now - (time || now); 46 | time = now; 47 | animator.tick(dt*0.001); 48 | }; 49 | render(); 50 | } catch(err) { 51 | console.log("error, so assuming we are in node environment", err); 52 | //node.js 53 | animator.play(canvas.save(width, height, "images/" + name + ".gif")); 54 | animator.ticker(Rx.Observable.return(0.1).repeat(Math.floor(frames))); 55 | } 56 | } 57 | 58 | export function sameExample(name: string, ref: string, cb: (boolean) => void) { 59 | try { 60 | throw new Error("not implemented"); 61 | } catch(err) { 62 | //node.js 63 | var cmp = require("file-compare"); 64 | var file1 = "images/" + name + ".gif"; 65 | var file2 = "images/" + ref + ".gif"; 66 | return cmp.compare(file1, file2, cb); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The HTML5 Herald 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 75 | 76 | -------------------------------------------------------------------------------- /dist/examples/lissajous.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(100, 100); 5 | //each frame, first draw black background to erase the previous contents 6 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 7 | /** 8 | * 9 | * A time varying set of 2D parametric equations. 10 | * https://en.wikipedia.org/wiki/Lissajous_curve. 11 | * Paramteric equations over P, P -> x = Asin(aP + i), y = Bsin(bP) 12 | * ( Normally parametric equations are described using parameter t, but this is confusing 13 | * with the time tick so I used P to describe the free parameter in the parametric equations) 14 | * At each time tick, t, a lissajous curve is generated from the time-varying A,a,B,b,i parameters 15 | */ 16 | function lissajous(A, a, B, b, i) { 17 | return Parameter.from(A).combine(function () { return function (A, a, B, b, i) { 18 | return [function (t) { return A * Math.sin(a * t + i); }, function (t) { return B * Math.sin(b * t); }]; 19 | }; }, Parameter.from(a), Parameter.from(B), Parameter.from(b), Parameter.from(i)); 20 | } 21 | var twoPi = 2 * Math.PI; 22 | // We use a numerical approximation 'trace' to sample enough P's to approximate the curve with a point list 23 | // So every time tick, we pick some time varying numbers chosen artistically 24 | // we pass them through lissajous to generate some parametric equations 25 | // we then trace that to turn it into a pointlist. 26 | // So we have transformed the (arbitary) time varying parameters, to a time varying list of points 27 | var timeVaryingPointList = Parameter.trace(lissajous(45, Parameter.t().mapValue(function (t) { return 2 + Math.sin(t); }), 45, Parameter.t().mapValue(function (t) { return 2 + Math.sin(t / 2); }), Parameter.t()), Parameter.t().mapValue(function (t) { return t % twoPi; }), Parameter.t().mapValue(function (t) { return (t % twoPi) + twoPi; }), 10, 4, 100000).mapValue(function (array) { 28 | return array.map(function (segment) { return segment.point; }); 29 | }); 30 | // To render a time varying list of points as a joined up line, each frame we chain a moveTo and many lineTo animations together. 31 | // As canvas animation persist over time forever, we have to use take(1) to limit the length of the animations to one frame. 32 | // playAll is able to play a new animation generated from a time varying stream of animations. 33 | // we can chain many animations based on values from a list, by reducing over the start of the animation chain. 34 | animator.play(Ax.create() 35 | .beginPath() 36 | .playAll(timeVaryingPointList.mapValue(// time varying point set is mapped to a time varying animation for squencing each frame 37 | function (pointList) { 38 | // Convert the list of points into a single animation chain 39 | return pointList.reduce(function (animation, point, index) { 40 | return index == 0 ? animation.moveTo(point) : animation.lineTo(point); 41 | }, 42 | // Start of chain, blank animation with take(1) 43 | // the take(1) ensure the played animation lasts 1 frame 44 | Ax.create().translate([50, 50]).take(1)); 45 | })) 46 | .strokeStyle("green") 47 | .lineWidth(3) 48 | .stroke()); 49 | helper.playExample("@name", 64, animator, 100, 100); 50 | -------------------------------------------------------------------------------- /dist/examples/example5.js: -------------------------------------------------------------------------------- 1 | var __extends = (this && this.__extends) || function (d, b) { 2 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 3 | function __() { this.constructor = d; } 4 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 5 | }; 6 | var Ax = require("../src/animaxe"); 7 | var helper = require("../src/helper"); 8 | var events = require("../src/events"); 9 | var animator = helper.getExampleAnimator(100, 100); 10 | /** 11 | * A Button is an animation but with extra mouse state attached 12 | */ 13 | var Button = (function (_super) { 14 | __extends(Button, _super); 15 | function Button(hotspot, // Babel doesn't like public modifier 16 | mouseState, // Babel doesn't like public modifier 17 | onMouseDown, onMouseOver, onIdle) { 18 | // we build a grand animation pipeline either side of the hot spot, 19 | // then we use the total pipeline's attach function as the attach function for this animation 20 | // so the constructed Button exposes a richer API (e.g. state) than a basic animation normally wouldn't 21 | _super.call(this, Ax.create() 22 | .if(mouseState.isMouseDown(), onMouseDown) // Condition the animation played based on mouse state 23 | .elif(mouseState.isMouseOver(), onMouseOver) 24 | .else(onIdle) 25 | .pipe(hotspot) 26 | .pipe(events.ComponentMouseEventHandler(mouseState)) 27 | .fill() 28 | .attach); 29 | this.hotspot = hotspot; 30 | this.mouseState = mouseState; 31 | mouseState.source = this; 32 | } 33 | /** 34 | * @param postprocessor hook to do things like attach listeners without breaking the animation chaining 35 | */ 36 | Button.rectangular = function (postprocessor) { 37 | var hotspot = Ax.create() 38 | .withinPath(Ax.create() 39 | .lineTo([40, 0]) 40 | .lineTo([40, 20]) 41 | .lineTo([0, 20]) 42 | .lineTo([0, 0])); 43 | var button = new Button(hotspot, new events.ComponentMouseState(), Ax.create().fillStyle("red"), /* pressed */ Ax.create().fillStyle("orange"), /* over */ Ax.create().fillStyle("white")); /* idle */ 44 | if (postprocessor) 45 | postprocessor(button); 46 | return button; 47 | }; 48 | return Button; 49 | })(Ax.Operation); 50 | //each frame, first draw black background to erase the previous contents 51 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 52 | animator.play(Ax.create() 53 | .translate([40, 40]) 54 | .rotate(Math.PI / 4) 55 | .pipe(Button.rectangular(function (button) { 56 | button.mouseState.mousedown.subscribe(function (evt) { return console.log("Button: mousedown", evt.animationCoord); }); 57 | button.mouseState.mouseup.subscribe(function (evt) { return console.log("Button: mouseup", evt.animationCoord); }); 58 | button.mouseState.mousemove.subscribe(function (evt) { return console.log("Button: mousemove", evt.animationCoord); }); 59 | button.mouseState.mouseenter.subscribe(function (evt) { return console.log("Button: mouseenter", evt.animationCoord); }); 60 | button.mouseState.mouseleave.subscribe(function (evt) { return console.log("Button: mouseleave", evt.animationCoord); }); 61 | }))); 62 | helper.playExample("@name", 2, animator, 100, 100); 63 | -------------------------------------------------------------------------------- /examples/example5.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 8 | 9 | /** 10 | * A Button is an animation but with extra mouse state attached 11 | */ 12 | class Button extends Ax.Operation { 13 | /** 14 | * @param postprocessor hook to do things like attach listeners without breaking the animation chaining 15 | */ 16 | static rectangular(postprocessor ?: (Button) => void): Button { // note Babel doesn't like this type 17 | var hotspot = Ax.create() 18 | .withinPath(Ax.create() 19 | .lineTo([ 40, 0]) 20 | .lineTo([ 40, 20]) 21 | .lineTo([ 0, 20]) 22 | .lineTo([ 0, 0]) 23 | ); 24 | 25 | var button = new Button( 26 | hotspot, 27 | new events.ComponentMouseState(), 28 | Ax.create().fillStyle("red"), /* pressed */ 29 | Ax.create().fillStyle("orange"), /* over */ 30 | Ax.create().fillStyle("white")); /* idle */ 31 | 32 | if (postprocessor) postprocessor(button); 33 | 34 | return button; 35 | } 36 | 37 | constructor(public hotspot: Ax.PathAnimation, // Babel doesn't like public modifier 38 | public mouseState: events.ComponentMouseState, // Babel doesn't like public modifier 39 | onMouseDown: Ax.Operation, 40 | onMouseOver: Ax.Operation, 41 | onIdle: Ax.Operation 42 | ) { 43 | // we build a grand animation pipeline either side of the hot spot, 44 | // then we use the total pipeline's attach function as the attach function for this animation 45 | // so the constructed Button exposes a richer API (e.g. state) than a basic animation normally wouldn't 46 | 47 | super(Ax.create() 48 | .if(mouseState.isMouseDown(), onMouseDown) // Condition the animation played based on mouse state 49 | .elif(mouseState.isMouseOver(), onMouseOver) 50 | .else(onIdle) 51 | .pipe(hotspot) 52 | .pipe(events.ComponentMouseEventHandler(mouseState)) 53 | .fill() 54 | .attach); 55 | mouseState.source = this; 56 | } 57 | } 58 | 59 | //each frame, first draw black background to erase the previous contents 60 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 61 | 62 | animator.play(Ax.create() 63 | .translate([40, 40]) 64 | .rotate(Math.PI / 4) 65 | .pipe(Button.rectangular( 66 | (button: Button) => { 67 | button.mouseState.mousedown.subscribe( 68 | (evt: events.AxMouseEvent) => console.log("Button: mousedown", evt.animationCoord)); 69 | button.mouseState.mouseup.subscribe( 70 | (evt: events.AxMouseEvent) => console.log("Button: mouseup", evt.animationCoord)); 71 | button.mouseState.mousemove.subscribe( 72 | (evt: events.AxMouseEvent) => console.log("Button: mousemove", evt.animationCoord)); 73 | button.mouseState.mouseenter.subscribe( 74 | (evt: events.AxMouseEvent) => console.log("Button: mouseenter", evt.animationCoord)); 75 | button.mouseState.mouseleave.subscribe( 76 | (evt: events.AxMouseEvent) => console.log("Button: mouseleave", evt.animationCoord)); 77 | } 78 | ) 79 | ) 80 | ); 81 | 82 | helper.playExample("@name", 2, animator, 100, 100); 83 | -------------------------------------------------------------------------------- /examples/example7.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 8 | 9 | /** 10 | * A Button is an animation but with extra mouse state attached 11 | */ 12 | class Button extends Ax.Operation { 13 | /** 14 | * @param postprocessor hook to do things like attach listeners without breaking the animation chaining 15 | */ 16 | static rectangular(postprocessor ?: (Button) => void): Button { // note Babel doesn't like this type 17 | var hotspot = Ax.create() 18 | .withinPath(Ax.create() 19 | .lineTo([ 40, 0]) 20 | .lineTo([ 40, 20]) 21 | .lineTo([ 0, 20]) 22 | .lineTo([ 0, 0]) 23 | ); 24 | 25 | var button = new Button( 26 | hotspot, 27 | new events.ComponentMouseState(), 28 | Ax.create().fillStyle("red"), /* pressed */ 29 | Ax.create().fillStyle("orange"), /* over */ 30 | Ax.create().fillStyle("white")); /* idle */ 31 | 32 | if (postprocessor) postprocessor(button); 33 | 34 | return button; 35 | } 36 | 37 | constructor(public hotspot: Ax.PathAnimation, // Babel doesn't like public modifier 38 | public mouseState: events.ComponentMouseState, // Babel doesn't like public modifier 39 | onMouseDown: Ax.Operation, 40 | onMouseOver: Ax.Operation, 41 | onIdle: Ax.Operation 42 | ) { 43 | // we build a grand animation pipeline either side of the hot spot, 44 | // then we use the total pipeline's attach function as the attach function for this animation 45 | // so the constructed Button exposes a richer API (e.g. state) than a basic animation normally wouldn't 46 | 47 | super(Ax.create() 48 | .if(mouseState.isMouseDown(), onMouseDown) // Condition the animation played based on mouse state 49 | .elif(mouseState.isMouseOver(), onMouseOver) 50 | .else(onIdle) 51 | .pipe(hotspot) 52 | .pipe(events.ComponentMouseEventHandler(mouseState)) 53 | .fill() 54 | .attach); 55 | mouseState.source = this; 56 | } 57 | } 58 | 59 | //each frame, first draw black background to erase the previous contents 60 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 61 | 62 | animator.play(Ax.create() 63 | .translate([40, 40]) 64 | .rotate(Math.PI / 4) 65 | .pipe(Button.rectangular( 66 | (button: Button) => { 67 | button.mouseState.mousedown.subscribe( 68 | (evt: events.AxMouseEvent) => console.log("Button: mousedown", evt.animationCoord)); 69 | button.mouseState.mouseup.subscribe( 70 | (evt: events.AxMouseEvent) => console.log("Button: mouseup", evt.animationCoord)); 71 | button.mouseState.mousemove.subscribe( 72 | (evt: events.AxMouseEvent) => console.log("Button: mousemove", evt.animationCoord)); 73 | button.mouseState.mouseenter.subscribe( 74 | (evt: events.AxMouseEvent) => console.log("Button: mouseenter", evt.animationCoord)); 75 | button.mouseState.mouseleave.subscribe( 76 | (evt: events.AxMouseEvent) => console.log("Button: mouseleave", evt.animationCoord)); 77 | } 78 | ) 79 | ) 80 | ); 81 | 82 | helper.playExample("@name", 2, animator, 100, 100); 83 | -------------------------------------------------------------------------------- /dist/test/example0.js: -------------------------------------------------------------------------------- 1 | // THIS IS AUTO GENERATED TEST CODE, DO NOT MODIFY DIRECTLY 2 | /// 3 | /// 4 | /// 5 | require('source-map-support').install(); 6 | require("should"); 7 | var Ax = require("../src/animaxe"); 8 | var helper = require("../src/helper"); 9 | var animator = helper.getExampleAnimator(); 10 | //each frame, first draw black background to erase the previous contents 11 | animator.play(Ax.create().fillStyle("#FF0000").fillRect([0, 0], [100, 100])); 12 | // the helper function pipes injects the context, either from a web canvas or a fake node.js one. 13 | helper.playExample("example0", 1, animator, 100, 100); 14 | describe('example0', function () { 15 | it('should match the reference', function (done) { 16 | helper.sameExample("example0", "example0-ref", function (equal) { 17 | equal.should.equal(true); 18 | done(); 19 | }); 20 | }); 21 | }); 22 | 23 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImV4YW1wbGUudGVtcGxhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkRBQTJEO0FBQzNELDZDQUE2QztBQUM3Qyw0Q0FBNEM7QUFDNUMsMkNBQTJDO0FBQzNDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ3hDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUdsQixJQUFZLEVBQUUsV0FBTSxnQkFBZ0IsQ0FBQyxDQUFBO0FBQ3JDLElBQVksTUFBTSxXQUFNLGVBQWUsQ0FBQyxDQUFBO0FBSXhDLElBQUksUUFBUSxHQUFnQixNQUFNLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztBQUV4RCx3RUFBd0U7QUFDeEUsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBQyxDQUFDLENBQUMsRUFBQyxDQUFDLEdBQUcsRUFBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFFMUUsaUdBQWlHO0FBQ2pHLE1BQU0sQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBRXRELFFBQVEsQ0FBQyxVQUFVLEVBQUU7SUFDakIsRUFBRSxDQUFFLDRCQUE0QixFQUFFLFVBQVMsSUFBSTtRQUMzQyxNQUFNLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxjQUFjLEVBQUUsVUFBUyxLQUFLO1lBQ3pELEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3pCLElBQUksRUFBRSxDQUFDO1FBQ1gsQ0FBQyxDQUFDLENBQUE7SUFDTixDQUFDLENBQUMsQ0FBQztBQUNQLENBQUMsQ0FBQyxDQUFDIiwiZmlsZSI6ImRpc3QvdGVzdC9leGFtcGxlMC5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFRISVMgSVMgQVVUTyBHRU5FUkFURUQgVEVTVCBDT0RFLCBETyBOT1QgTU9ESUZZIERJUkVDVExZXG4vLy8gPHJlZmVyZW5jZSBwYXRoPVwiLi4vdHlwZXMvc2hvdWxkLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL21vY2hhLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL25vZGUuZC50c1wiIC8+XG5yZXF1aXJlKCdzb3VyY2UtbWFwLXN1cHBvcnQnKS5pbnN0YWxsKCk7XG5yZXF1aXJlKFwic2hvdWxkXCIpO1xuXG5pbXBvcnQgKiBhcyBSeCBmcm9tIFwicnhcIjtcbmltcG9ydCAqIGFzIEF4IGZyb20gXCIuLi9zcmMvYW5pbWF4ZVwiO1xuaW1wb3J0ICogYXMgaGVscGVyIGZyb20gXCIuLi9zcmMvaGVscGVyXCI7XG5pbXBvcnQgKiBhcyBldmVudHMgZnJvbSBcIi4uL3NyYy9ldmVudHNcIjtcbmltcG9ydCAqIGFzIFBhcmFtZXRlciBmcm9tIFwiLi4vc3JjL1BhcmFtZXRlclwiO1xuXG52YXIgYW5pbWF0b3I6IEF4LkFuaW1hdG9yID0gaGVscGVyLmdldEV4YW1wbGVBbmltYXRvcigpO1xuXG4vL2VhY2ggZnJhbWUsIGZpcnN0IGRyYXcgYmxhY2sgYmFja2dyb3VuZCB0byBlcmFzZSB0aGUgcHJldmlvdXMgY29udGVudHNcbmFuaW1hdG9yLnBsYXkoQXguY3JlYXRlKCkuZmlsbFN0eWxlKFwiI0ZGMDAwMFwiKS5maWxsUmVjdChbMCwwXSxbMTAwLDEwMF0pKTtcblxuLy8gdGhlIGhlbHBlciBmdW5jdGlvbiBwaXBlcyBpbmplY3RzIHRoZSBjb250ZXh0LCBlaXRoZXIgZnJvbSBhIHdlYiBjYW52YXMgb3IgYSBmYWtlIG5vZGUuanMgb25lLlxuaGVscGVyLnBsYXlFeGFtcGxlKFwiZXhhbXBsZTBcIiwgMSwgYW5pbWF0b3IsIDEwMCwgMTAwKTtcblxuZGVzY3JpYmUoJ2V4YW1wbGUwJywgZnVuY3Rpb24gKCkge1xuICAgIGl0ICgnc2hvdWxkIG1hdGNoIHRoZSByZWZlcmVuY2UnLCBmdW5jdGlvbihkb25lKSB7XG4gICAgICAgIGhlbHBlci5zYW1lRXhhbXBsZShcImV4YW1wbGUwXCIsIFwiZXhhbXBsZTAtcmVmXCIsIGZ1bmN0aW9uKGVxdWFsKSB7XG4gICAgICAgICAgICBlcXVhbC5zaG91bGQuZXF1YWwodHJ1ZSk7XG4gICAgICAgICAgICBkb25lKCk7XG4gICAgICAgIH0pXG4gICAgfSk7XG59KTsiXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= 24 | -------------------------------------------------------------------------------- /src/ctx-get-transform.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This code was ported from https://github.com/tmpvar/ctx-get-transform 3 | in order to get it to work with ES6 properly 4 | 5 | The MIT License (MIT) 6 | Copyright © 2014 Elijah Insua 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the “Software”), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | */ 26 | 27 | // the reference file just declares var mat3 with any type to avoid typing this module 28 | /// 29 | import mat3 = require('gl-mat3'); 30 | 31 | export function monkeyPatchCtxToAddGetTransform(ctx) { 32 | 33 | var mat = [1, 0, 0, 0, 1, 0, 0, 0, 1]; 34 | var stack = []; 35 | var v2scratch = [0, 0, 0]; 36 | var m3scratch = [1, 0, 0, 0, 1, 0, 0, 0, 1]; 37 | 38 | ctx.getTransform = function tGetTransform() { 39 | return mat; 40 | }; 41 | 42 | (function(save) { 43 | ctx.save = function tSave(){ 44 | stack.push(mat3.clone(mat)); 45 | return save.call(ctx); 46 | }; 47 | })(ctx.save); 48 | 49 | (function(restore) { 50 | ctx.restore = function tRestore(){ 51 | mat = stack.pop(); 52 | return restore.call(ctx); 53 | }; 54 | })(ctx.restore); 55 | 56 | (function(scale) { 57 | ctx.scale = function tScale(sx, sy){ 58 | v2scratch[0] = sx; 59 | v2scratch[1] = sy; 60 | mat3.scale(mat, mat, v2scratch); 61 | return scale.call(ctx, sx, sy); 62 | }; 63 | })(ctx.scale); 64 | 65 | (function(rotate) { 66 | ctx.rotate = function tRotate(radians){ 67 | mat3.rotate(mat, mat, radians); 68 | return rotate.call(ctx, radians); 69 | }; 70 | })(ctx.rotate); 71 | 72 | (function(translate) { 73 | ctx.translate = function tTranslate(dx, dy){ 74 | v2scratch[0] = dx; 75 | v2scratch[1] = dy; 76 | 77 | mat3.translate(mat, mat, v2scratch); 78 | return translate.call(ctx, dx, dy); 79 | }; 80 | })(ctx.translate); 81 | 82 | (function(transform) { 83 | ctx.transform = function tTransform(a, b, c, d, e, f){ 84 | m3scratch[0] = a; 85 | m3scratch[1] = c; 86 | m3scratch[2] = e; 87 | m3scratch[3] = b; 88 | m3scratch[4] = d; 89 | m3scratch[5] = f; 90 | 91 | mat3.multiply(mat, mat, m3scratch); 92 | return transform.call(ctx, a, b, c, d, e, f); 93 | }; 94 | })(ctx.transform); 95 | 96 | (function(setTransform) { 97 | ctx.setTransform = function tSetTransform(a, b, c, d, e, f){ 98 | mat[0] = a; 99 | mat[1] = c; 100 | mat[2] = e; 101 | mat[3] = b; 102 | mat[4] = d; 103 | mat[5] = f; 104 | return setTransform.call(ctx, a, b, c, d, e, f); 105 | }; 106 | })(ctx.setTransform); 107 | 108 | return ctx; 109 | } 110 | 111 | -------------------------------------------------------------------------------- /src/zip.ts: -------------------------------------------------------------------------------- 1 | import * as utils from "./utils" 2 | import * as Rx from "rx" 3 | 4 | function falseFactory() { return false; } 5 | function emptyArrayFactory() { return []; } 6 | function notEmpty(x) { return x.length > 0; } 7 | function shiftEach(x) { return x.shift(); } 8 | function emptyAndDone(qd) { return qd[0].length == 0 && qd[1] === true; } 9 | function notTheSame(i) { 10 | return function (x, j) { 11 | return j !== i; 12 | }; 13 | } 14 | function zipArrays(arrays) { 15 | return arrays[0].map(function(_,i) { 16 | return arrays.map(function(array) { return array[i]; }) 17 | } 18 | ); 19 | } 20 | 21 | 22 | class ZipObservable { 23 | subscriptions: Rx.IDisposable[]; 24 | constructor(public observer, public sources, public resultSelector) { 25 | // console.log("ZipObservable"); 26 | var n = this.sources.length; 27 | var done = utils.arrayInitialize(n, falseFactory); 28 | var q = utils.arrayInitialize(n, emptyArrayFactory); 29 | this.subscriptions = new Array(n); 30 | 31 | for (var i = 0; i < n; i++) { 32 | var source = this.sources[i] 33 | var subscriber = new ZipObserver(observer, i, this, q, done); 34 | this.subscriptions[i] = source.subscribe( 35 | subscriber.next.bind(subscriber), 36 | subscriber.error.bind(subscriber), 37 | subscriber.completed.bind(subscriber) 38 | ) 39 | } 40 | } 41 | 42 | 43 | dispose() { 44 | for (var i = 0; i < this.sources.length; i++) { 45 | this.subscriptions[i].dispose(); 46 | } 47 | } 48 | } 49 | 50 | var errorObject = {value: null}; 51 | function tryCatch(fn, ctx, args) { 52 | try { 53 | return fn.apply(ctx, args); 54 | } 55 | catch(e) { 56 | errorObject.value = e; 57 | return errorObject; 58 | } 59 | } 60 | 61 | class ZipObserver { 62 | _o; 63 | _i; 64 | _p; 65 | _q; 66 | _d; 67 | constructor(o, i, p, q, d) { 68 | this._o = o; 69 | this._i = i; 70 | this._p = p; 71 | this._q = q; 72 | this._d = d; 73 | } 74 | 75 | next(x) { 76 | //console.log("ZipObserver: next", this); 77 | this._q[this._i].push(x); 78 | if (this._q.every(notEmpty)) { 79 | var queuedValues = this._q.map(shiftEach); 80 | var res = tryCatch(this._p.resultSelector, null, queuedValues); 81 | 82 | if (res === errorObject) { 83 | this._o.onError(res.value); 84 | } else { 85 | this._o.onNext(res); 86 | // Any done and empty => zip completed. 87 | if (zipArrays([this._q, this._d]).some(emptyAndDone)) { 88 | this._o.onCompleted(); 89 | } 90 | } 91 | } 92 | }; 93 | 94 | error(e) { 95 | this._o.onError(e); 96 | }; 97 | 98 | completed() { 99 | this._d[this._i] = true; // Done... 100 | if (this._q[this._i].length == 0) { // ...and empty => zip completed. 101 | this._o.onCompleted(); 102 | } 103 | }; 104 | } 105 | /** 106 | * Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences or an array have produced an element at a corresponding index. 107 | * The last element in the arguments must be a function to invoke for each series of elements at corresponding indexes in the args. 108 | * @returns {Observable} An observable sequence containing the result of combining elements of the args using the specified result selector function. 109 | */ 110 | export function zip( 111 | resultSelector: (...sourcesValues: any[]) => T, 112 | ...sources: Rx.Observable[]): Rx.Observable { 113 | return >Rx.Observable.create( 114 | observer => { 115 | var zipper = new ZipObservable(observer, sources, resultSelector) 116 | return zipper.dispose.bind(zipper); 117 | } 118 | ); 119 | }; 120 | -------------------------------------------------------------------------------- /examples/lissajous.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | import * as parametric from "../src/parametric"; 7 | import * as OT from "../src/frp"; // TODO this should be in Ax 8 | import * as types from "../src/types"; // TODO this should be in Ax 9 | import * as canvas from "../src/canvas"; // TODO this should be in Ax 10 | 11 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 12 | 13 | //each frame, first draw black background to erase the previous contents 14 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 15 | 16 | /** 17 | * 18 | * A time varying set of 2D parametric equations. 19 | * https://en.wikipedia.org/wiki/Lissajous_curve. 20 | * Paramteric equations over P, P -> x = Asin(aP + i), y = Bsin(bP) 21 | * ( Normally parametric equations are described using parameter t, but this is confusing 22 | * with the time tick so I used P to describe the free parameter in the parametric equations) 23 | * At each time tick, t, a lissajous curve is generated from the time-varying A,a,B,b,i parameters 24 | */ 25 | function lissajous( 26 | A: Ax.NumberArg, a: Ax.NumberArg, 27 | B: Ax.NumberArg, b: Ax.NumberArg, 28 | i: Ax.NumberArg) 29 | : Parameter.Parameter<((t: number) => number)[]> { 30 | return Parameter.from(A).combine( 31 | () => (A: number, a: number, B: number, b: number, i: number) => 32 | [(t: number) => A * Math.sin(a * t + i), (t: number) => B * Math.sin(b * t)], 33 | Parameter.from(a), 34 | Parameter.from(B), 35 | Parameter.from(b), 36 | Parameter.from(i) 37 | ) 38 | } 39 | 40 | var twoPi = 2 * Math.PI; 41 | 42 | // We use a numerical approximation 'trace' to sample enough P's to approximate the curve with a point list 43 | // So every time tick, we pick some time varying numbers chosen artistically 44 | // we pass them through lissajous to generate some parametric equations 45 | // we then trace that to turn it into a pointlist. 46 | // So we have transformed the (arbitary) time varying parameters, to a time varying list of points 47 | var timeVaryingPointList: Parameter.Parameter = 48 | Parameter.trace( 49 | lissajous(45, Parameter.t().mapValue(t => 2 + Math.sin(t)), 45, Parameter.t().mapValue(t => 2 + Math.sin(t/2)), Parameter.t()), 50 | Parameter.t().mapValue(t => t % twoPi), Parameter.t().mapValue(t => (t % twoPi) + twoPi), 51 | 10, 52 | 4, 53 | 100000 54 | ).mapValue( 55 | (array: {point: number[], t: number}[]) => { 56 | return array.map(segment => segment.point); 57 | } 58 | ); 59 | 60 | 61 | // To render a time varying list of points as a joined up line, each frame we chain a moveTo and many lineTo animations together. 62 | // As canvas animation persist over time forever, we have to use take(1) to limit the length of the animations to one frame. 63 | // playAll is able to play a new animation generated from a time varying stream of animations. 64 | // we can chain many animations based on values from a list, by reducing over the start of the animation chain. 65 | animator.play( 66 | Ax.create() 67 | //.translate([50, 50]) BUG THIS DOESN'T WORK HERE? 68 | .beginPath() 69 | .playAll( 70 | timeVaryingPointList.mapValue( // time varying point set is mapped to a time varying animation for squencing each frame 71 | (pointList: number[][]) => { 72 | // Convert the list of points into a single animation chain 73 | return pointList.reduce((animation, point: number[], index:number) => { 74 | return index == 0 ? animation.moveTo(point): animation.lineTo(point) 75 | }, 76 | // Start of chain, blank animation with take(1) 77 | // the take(1) ensure the played animation lasts 1 frame 78 | Ax.create().translate([50, 50]).take(1)); 79 | } 80 | ) 81 | ) 82 | .strokeStyle("green") 83 | .lineWidth(3) 84 | .stroke() 85 | ); 86 | 87 | helper.playExample("@name", 64, animator, 100, 100); 88 | 89 | -------------------------------------------------------------------------------- /dist/src/intersection.js: -------------------------------------------------------------------------------- 1 | var frp = require('./frp'); 2 | var zip = require("./zip"); 3 | // stream intersection &&& in AFRP 4 | function intersection(a, b) { 5 | return new frp.SignalFn(function (upstream) { 6 | var a_out = a.attach(upstream); 7 | var b_out = b.attach(upstream); 8 | return zip.zip(function (values) { return extend(values[0], values[1]); }, a_out, b_out); 9 | }); 10 | } 11 | exports.intersection = intersection; 12 | function extend(first, second) { 13 | var result = {}; 14 | for (var id in first) { 15 | result[id] = first[id]; 16 | } 17 | for (var id in second) { 18 | if (!result.hasOwnProperty(id)) { 19 | result[id] = second[id]; 20 | } 21 | } 22 | return result; 23 | } 24 | function first(sf) { 25 | return new frp.SignalFn(function (upstream) { 26 | var first = sf.attach(upstream); 27 | var second = upstream; 28 | return zip.zip(function (values) { return extend(values[0], values[1]); }, first, second); 29 | }); 30 | } 31 | exports.first = first; 32 | function arr(fn) { 33 | return new frp.SignalFn(function (upstream) { return upstream.map(fn); }); 34 | } 35 | exports.arr = arr; 36 | /// >>> operator 37 | /* 38 | export function pipe(first: frp.SignalFn, second: frp.SignalFn): frp.SignalFn { 39 | return new frp.SignalFn( 40 | (upstream: Rx.Observable) => second.attach(first.attach(upstream)) 41 | ); 42 | } 43 | 44 | /// >>loop operator 45 | export function loop(first: frp.SignalFn, second: frp.SignalFn): frp.SignalFn { 46 | return new frp.SignalFn( 47 | (upstream: Rx.Observable) => second.attach(first.attach(upstream)) 48 | ); 49 | }*/ 50 | 51 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9pbnRlcnNlY3Rpb24udHMiXSwibmFtZXMiOlsiaW50ZXJzZWN0aW9uIiwiZXh0ZW5kIiwiZmlyc3QiLCJhcnIiXSwibWFwcGluZ3MiOiJBQUFBLElBQVksR0FBRyxXQUFNLE9BQU8sQ0FBQyxDQUFBO0FBRTdCLElBQVksR0FBRyxXQUFNLE9BR3JCLENBQUMsQ0FIMkI7QUFFNUIsa0NBQWtDO0FBQ2xDLHNCQUNLLENBQXlCLEVBQUUsQ0FBeUI7SUFDakRBLE1BQU1BLENBQUNBLElBQUlBLEdBQUdBLENBQUNBLFFBQVFBLENBQ25CQSxVQUFDQSxRQUEyQkE7UUFDeEJBLElBQUlBLEtBQUtBLEdBQXdCQSxDQUFDQSxDQUFDQSxNQUFNQSxDQUFDQSxRQUFRQSxDQUFDQSxDQUFDQTtRQUNwREEsSUFBSUEsS0FBS0EsR0FBd0JBLENBQUNBLENBQUNBLE1BQU1BLENBQUNBLFFBQVFBLENBQUNBLENBQUNBO1FBQ3BEQSxNQUFNQSxDQUFDQSxHQUFHQSxDQUFDQSxHQUFHQSxDQUNWQSxVQUFDQSxNQUFvQkEsSUFBS0EsT0FBQUEsTUFBTUEsQ0FBQ0EsTUFBTUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsRUFBRUEsTUFBTUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsRUFBNUJBLENBQTRCQSxFQUN0REEsS0FBS0EsRUFBRUEsS0FBS0EsQ0FBQ0EsQ0FBQ0E7SUFDdEJBLENBQUNBLENBQ0pBLENBQUFBO0FBRVRBLENBQUNBO0FBWmUsb0JBQVksZUFZM0IsQ0FBQTtBQUVELGdCQUFzQixLQUFRLEVBQUUsTUFBUztJQUNyQ0MsSUFBSUEsTUFBTUEsR0FBV0EsRUFBRUEsQ0FBQ0E7SUFDeEJBLEdBQUdBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLEVBQUVBLElBQUlBLEtBQUtBLENBQUNBLENBQUNBLENBQUNBO1FBQ25CQSxNQUFNQSxDQUFDQSxFQUFFQSxDQUFDQSxHQUFHQSxLQUFLQSxDQUFDQSxFQUFFQSxDQUFDQSxDQUFDQTtJQUMzQkEsQ0FBQ0E7SUFDREEsR0FBR0EsQ0FBQ0EsQ0FBQ0EsR0FBR0EsQ0FBQ0EsRUFBRUEsSUFBSUEsTUFBTUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7UUFDcEJBLEVBQUVBLENBQUNBLENBQUNBLENBQUNBLE1BQU1BLENBQUNBLGNBQWNBLENBQUNBLEVBQUVBLENBQUNBLENBQUNBLENBQUNBLENBQUNBO1lBQzdCQSxNQUFNQSxDQUFDQSxFQUFFQSxDQUFDQSxHQUFHQSxNQUFNQSxDQUFDQSxFQUFFQSxDQUFDQSxDQUFDQTtRQUM1QkEsQ0FBQ0E7SUFDTEEsQ0FBQ0E7SUFDREEsTUFBTUEsQ0FBQ0EsTUFBTUEsQ0FBQ0E7QUFDbEJBLENBQUNBO0FBRUQsZUFDSyxFQUF5QjtJQUN0QkMsTUFBTUEsQ0FBQ0EsSUFBSUEsR0FBR0EsQ0FBQ0EsUUFBUUEsQ0FDbkJBLFVBQUNBLFFBQXlDQTtRQUN0Q0EsSUFBSUEsS0FBS0EsR0FBdUJBLEVBQUVBLENBQUNBLE1BQU1BLENBQUNBLFFBQVFBLENBQUNBLENBQUNBO1FBQ3BEQSxJQUFJQSxNQUFNQSxHQUErQkEsUUFBUUEsQ0FBQ0E7UUFDbERBLE1BQU1BLENBQUNBLEdBQUdBLENBQUNBLEdBQUdBLENBQ1ZBLFVBQUNBLE1BQTBCQSxJQUFLQSxPQUFBQSxNQUFNQSxDQUFDQSxNQUFNQSxDQUFDQSxDQUFDQSxDQUFDQSxFQUFFQSxNQUFNQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxFQUE1QkEsQ0FBNEJBLEVBQzVEQSxLQUFLQSxFQUFFQSxNQUFNQSxDQUFDQSxDQUFDQTtJQUN2QkEsQ0FBQ0EsQ0FDSkEsQ0FBQUE7QUFDVEEsQ0FBQ0E7QUFYZSxhQUFLLFFBV3BCLENBQUE7QUFFRCxhQUFrRCxFQUFzQjtJQUNwRUMsTUFBTUEsQ0FBQ0EsSUFBSUEsR0FBR0EsQ0FBQ0EsUUFBUUEsQ0FDbkJBLFVBQUNBLFFBQTJCQSxJQUFLQSxPQUFBQSxRQUFRQSxDQUFDQSxHQUFHQSxDQUFDQSxFQUFFQSxDQUFDQSxFQUFoQkEsQ0FBZ0JBLENBQ3BEQSxDQUFDQTtBQUNOQSxDQUFDQTtBQUplLFdBQUcsTUFJbEIsQ0FBQTtBQUNELGdCQUFnQjtBQUNoQjs7Ozs7Ozs7Ozs7O0dBWUciLCJmaWxlIjoic3JjL2ludGVyc2VjdGlvbi5qcyIsInNvdXJjZXNDb250ZW50IjpbbnVsbF0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 52 | -------------------------------------------------------------------------------- /dist/examples/example6.js: -------------------------------------------------------------------------------- 1 | var __extends = (this && this.__extends) || function (d, b) { 2 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 3 | function __() { this.constructor = d; } 4 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 5 | }; 6 | var Rx = require("rx"); 7 | var Ax = require("../src/animaxe"); 8 | var helper = require("../src/helper"); 9 | var events = require("../src/events"); 10 | var Parameter = require("../src/Parameter"); 11 | var animator = helper.getExampleAnimator(100, 100); 12 | // todo 13 | // make add a max and min value 14 | // scale the value 15 | // make the slider impervious to rotation (mouse movement needs to be transformed by transform matrix-1 ) 16 | /** 17 | * A Button is an animation but with extra mouse state attached 18 | */ 19 | var Slider = (function (_super) { 20 | __extends(Slider, _super); 21 | function Slider(hotspot, // Babel doesn't like public modifier 22 | knobMouseState, // Babel doesn't like public modifier 23 | canvasMouseState, // Babel doesn't like public modifier 24 | value, onMouseDownKnob, onMouseOverKnob, onIdleKnob) { 25 | // we build a grand animation pipeline either side of the hot spot, 26 | // then we use the total pipeline's attach function as the attach function for this animation 27 | // so the constructed Button exposes a richer API (e.g. state) than a basic animation normally wouldn't 28 | // todo slider value is not changed relatively 29 | _super.call(this, Ax.create() 30 | .pipe(events.CanvasMouseEventHandler(canvasMouseState)) //global mouse listener 31 | .parallel([ 32 | Ax.create() 33 | .translate(Parameter.point(0, Parameter.updateFrom(0, value))) 34 | .if(knobMouseState.isMouseDown(), onMouseDownKnob) // Condition the animation played based on mouse state 35 | .elif(knobMouseState.isMouseOver(), onMouseOverKnob) 36 | .else(onIdleKnob) 37 | .pipe(hotspot) 38 | .pipe(events.ComponentMouseEventHandler(knobMouseState)) 39 | .fill(), 40 | Ax.create() //todo, the slider 41 | ]) 42 | .attach); 43 | this.hotspot = hotspot; 44 | this.knobMouseState = knobMouseState; 45 | this.canvasMouseState = canvasMouseState; 46 | knobMouseState.source = this; 47 | this.value = value; 48 | this.value.subscribe(function (x) { return console.log("slider value changed", x); }); 49 | // a stream of points indicating the start of the slide move, or null if a slide move is not in progress 50 | var startSlideStream = Rx.Observable.merge([ 51 | knobMouseState.mousedown 52 | .withLatestFrom(value, function (evt, value) { 53 | return { eventStart: evt, valueStart: value }; 54 | }), 55 | canvasMouseState.mouseup.map(function (evt) { return null; }) 56 | ]); 57 | // a stream of number or null, indicating the new value of the slider, or null to mean no change 58 | var slideChangeStream = Rx.Observable.combineLatest(startSlideStream, canvasMouseState.mousemove, function (start, current) { 59 | return start == null ? null : current.canvasCoord[1] - start.eventStart.canvasCoord[1] + start.valueStart; 60 | }).filter(function (val) { return val != null; }); 61 | // remove the nulls from the stream, and pipe into the value for the slider. 62 | slideChangeStream.tap(function (v) { return console.log("slider: value", v); }, function (err) { return console.error(err); }) 63 | .subscribe(this.value); 64 | } 65 | /** 66 | * @param postprocessor hook to do things like attach listeners without breaking the animation chaining 67 | */ 68 | Slider.rectangular = function (value, postprocessor) { 69 | var hotspot = Ax.create() 70 | .withinPath(Ax.create() 71 | .lineTo([20, 0]) 72 | .lineTo([20, 20]) 73 | .lineTo([0, 20]) 74 | .lineTo([0, 0])); 75 | value.subscribe(function (x) { return console.log("pre construction value changed", x); }); 76 | var slider = new Slider(hotspot, new events.ComponentMouseState(), new events.ComponentMouseState(), value, Ax.create().fillStyle("red"), /* pressed */ Ax.create().fillStyle("orange"), /* over */ Ax.create().fillStyle("white") /* idle */); /* idle */ 77 | if (postprocessor) 78 | postprocessor(slider); 79 | return slider; 80 | }; 81 | return Slider; 82 | })(Ax.Operation); 83 | var value = new Rx.BehaviorSubject(0); 84 | //each frame, first draw black background to erase the previous contents 85 | animator.play(Ax.create().fillStyle(Parameter.rgba(Parameter.updateFrom(0, value).mapValue(function (x) { return x * 2.5; }), 0, 0, 1)).fillRect([0, 0], [100, 100])); 86 | animator.play(Ax.create() 87 | .pipe(Slider.rectangular(value))); 88 | helper.playExample("@name", 2, animator, 100, 100); 89 | -------------------------------------------------------------------------------- /types/should.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for should.js 3.1.2 2 | // Project: https://github.com/visionmedia/should.js 3 | // Definitions by: Alex Varju , Maxime LUCE 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | interface Object { 7 | should: ShouldAssertion; 8 | } 9 | 10 | interface ShouldAssertion { 11 | // basic grammar 12 | a: ShouldAssertion; 13 | an: ShouldAssertion; 14 | and: ShouldAssertion; 15 | be: ShouldAssertion; 16 | have: ShouldAssertion; 17 | with: ShouldAssertion; 18 | of: ShouldAssertion; 19 | not: ShouldAssertion; 20 | 21 | // validators 22 | arguments: ShouldAssertion; 23 | empty: ShouldAssertion; 24 | ok: ShouldAssertion; 25 | true: ShouldAssertion; 26 | false: ShouldAssertion; 27 | NaN: ShouldAssertion; 28 | Infinity: ShouldAssertion; 29 | Array: ShouldAssertion; 30 | Object: ShouldAssertion; 31 | String: ShouldAssertion; 32 | Boolean: ShouldAssertion; 33 | Number: ShouldAssertion; 34 | Error: ShouldAssertion; 35 | Function: ShouldAssertion; 36 | eql(expected: any, description?: string): ShouldAssertion; 37 | equal(expected: any, description?: string): ShouldAssertion; 38 | within(start: number, finish: number, description?: string): ShouldAssertion; 39 | approximately(value: number, delta: number, description?: string): ShouldAssertion; 40 | type(expected: any, description?: string): ShouldAssertion; 41 | instanceof(constructor: Function, description?: string): ShouldAssertion; 42 | above(n: number, description?: string): ShouldAssertion; 43 | below(n: number, description?: string): ShouldAssertion; 44 | match(other: {}, description?: string): ShouldAssertion; 45 | match(other: (val: any) => any, description?: string): ShouldAssertion; 46 | match(regexp: RegExp, description?: string): ShouldAssertion; 47 | match(other: any, description?: string): ShouldAssertion; 48 | matchEach(other: {}, description?: string): ShouldAssertion; 49 | matchEach(other: (val: any) => any, description?: string): ShouldAssertion; 50 | matchEach(regexp: RegExp, description?: string): ShouldAssertion; 51 | matchEach(other: any, description?: string): ShouldAssertion; 52 | length(n: number, description?: string): ShouldAssertion; 53 | property(name: string, description?: string): ShouldAssertion; 54 | property(name: string, val: any, description?: string): ShouldAssertion; 55 | properties(names: string[]): ShouldAssertion; 56 | properties(name: string): ShouldAssertion; 57 | properties(descriptor: any): ShouldAssertion; 58 | properties(...properties: string[]): ShouldAssertion; 59 | ownProperty(name: string, description?: string): ShouldAssertion; 60 | contain(obj: any): ShouldAssertion; 61 | containEql(obj: any): ShouldAssertion; 62 | containDeep(obj: any): ShouldAssertion; 63 | keys(...allKeys: string[]): ShouldAssertion; 64 | keys(allKeys: string[]): ShouldAssertion; 65 | header(field: string, val?: string): ShouldAssertion; 66 | status(code: number): ShouldAssertion; 67 | json: ShouldAssertion; 68 | html: ShouldAssertion; 69 | startWith(expected: string, message?: any): ShouldAssertion; 70 | endWith(expected: string, message?: any): ShouldAssertion; 71 | throw(message?: any): ShouldAssertion; 72 | 73 | // deprecated 74 | include(obj: any, description?: string): ShouldAssertion; 75 | includeEql(obj: any[], description?: string): ShouldAssertion; 76 | 77 | // aliases 78 | exactly(expected: any, description?: string): ShouldAssertion; 79 | instanceOf(constructor: Function, description?: string): ShouldAssertion; 80 | throwError(message?: any): ShouldAssertion; 81 | lengthOf(n: number, description?: string): ShouldAssertion; 82 | key(key: string): ShouldAssertion; 83 | haveOwnProperty(name: string, description?: string): ShouldAssertion; 84 | greaterThan(n: number, description?: string): ShouldAssertion; 85 | lessThan(n: number, description?: string): ShouldAssertion; 86 | } 87 | 88 | interface ShouldInternal { 89 | // should.js's extras 90 | exist(actual: any, msg?: string): void; 91 | exists(actual: any, msg?: string): void; 92 | not: ShouldInternal; 93 | } 94 | 95 | interface Internal extends ShouldInternal { 96 | (obj: any): ShouldAssertion; 97 | 98 | // node.js's assert functions 99 | fail(actual: any, expected: any, message: string, operator: string): void; 100 | assert(value: any, message: string): void; 101 | ok(value: any, message?: string): void; 102 | equal(actual: any, expected: any, message?: string): void; 103 | notEqual(actual: any, expected: any, message?: string): void; 104 | deepEqual(actual: any, expected: any, message?: string): void; 105 | notDeepEqual(actual: any, expected: any, message?: string): void; 106 | strictEqual(actual: any, expected: any, message?: string): void; 107 | notStrictEqual(actual: any, expected: any, message?: string): void; 108 | throws(block: any, error?: any, message?: string): void; 109 | doesNotThrow(block: any, message?: string): void; 110 | ifError(value: any): void; 111 | inspect(value: any, obj: any): any; 112 | } 113 | 114 | declare var should: Internal; 115 | declare var Should: Internal; 116 | interface Window { 117 | Should: Internal; 118 | } 119 | 120 | declare module "should" { 121 | export = should; 122 | } 123 | -------------------------------------------------------------------------------- /dist/test/skew.js: -------------------------------------------------------------------------------- 1 | // THIS IS AUTO GENERATED TEST CODE, DO NOT MODIFY DIRECTLY 2 | /// 3 | /// 4 | /// 5 | require('source-map-support').install(); 6 | require("should"); 7 | var Ax = require("../src/animaxe"); 8 | var helper = require("../src/helper"); 9 | var Parameter = require("../src/Parameter"); 10 | var animator = helper.getExampleAnimator(100, 100); 11 | //each frame, first draw black background to erase the previous contents 12 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 13 | // we draw single pixels of different hues moving on a circle circumference 14 | animator.play(Ax.create().parallel(Ax.range(0, 5).map(function (offset) { return Ax.create() 15 | .skewT(offset * 0.1) 16 | .translate(Parameter.point(Parameter.sin(Parameter.t()).mapValue(function (x) { return 45 * (x + 1); }), Parameter.cos(Parameter.t()).mapValue(function (x) { return 45 * (x + 1); }))) 17 | .fillStyle("white").fillRect([-1, -1], [3, 3]); }))); 18 | helper.playExample("skew", 20, animator, 100, 100); 19 | describe('skew', function () { 20 | it('should match the reference', function (done) { 21 | helper.sameExample("skew", "skew-ref", function (equal) { 22 | equal.should.equal(true); 23 | done(); 24 | }); 25 | }); 26 | }); 27 | 28 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImV4YW1wbGUudGVtcGxhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkRBQTJEO0FBQzNELDZDQUE2QztBQUM3Qyw0Q0FBNEM7QUFDNUMsMkNBQTJDO0FBQzNDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ3hDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUdsQixJQUFZLEVBQUUsV0FBTSxnQkFBZ0IsQ0FBQyxDQUFBO0FBQ3JDLElBQVksTUFBTSxXQUFNLGVBQWUsQ0FBQyxDQUFBO0FBRXhDLElBQVksU0FBUyxXQUFNLGtCQUFrQixDQUFDLENBQUE7QUFFOUMsSUFBSSxRQUFRLEdBQWdCLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFFaEUsd0VBQXdFO0FBQ3hFLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxHQUFHLEVBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRTFFLDJFQUEyRTtBQUMzRSxRQUFRLENBQUMsSUFBSSxDQUNULEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQ2hCLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FDZCxVQUFBLE1BQU0sSUFBSSxPQUFBLEVBQUUsQ0FBQyxNQUFNLEVBQUU7S0FDaEIsS0FBSyxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUM7S0FDbkIsU0FBUyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQ2xCLFNBQVMsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLFVBQUEsQ0FBQyxJQUFJLE9BQUEsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFaLENBQVksQ0FBQyxFQUN4RCxTQUFTLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFBLENBQUMsSUFBSSxPQUFBLEVBQUUsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBWixDQUFZLENBQUMsQ0FDM0QsQ0FDSjtLQUNBLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFDLENBQUMsQ0FBQyxDQUFDLEVBUHZDLENBT3VDLENBQ3BELENBQ0osQ0FDSixDQUFDO0FBRUYsTUFBTSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFHbkQsUUFBUSxDQUFDLE1BQU0sRUFBRTtJQUNiLEVBQUUsQ0FBRSw0QkFBNEIsRUFBRSxVQUFTLElBQUk7UUFDM0MsTUFBTSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLFVBQVMsS0FBSztZQUNqRCxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUN6QixJQUFJLEVBQUUsQ0FBQztRQUNYLENBQUMsQ0FBQyxDQUFBO0lBQ04sQ0FBQyxDQUFDLENBQUM7QUFDUCxDQUFDLENBQUMsQ0FBQyIsImZpbGUiOiJkaXN0L3Rlc3Qvc2tldy5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFRISVMgSVMgQVVUTyBHRU5FUkFURUQgVEVTVCBDT0RFLCBETyBOT1QgTU9ESUZZIERJUkVDVExZXG4vLy8gPHJlZmVyZW5jZSBwYXRoPVwiLi4vdHlwZXMvc2hvdWxkLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL21vY2hhLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL25vZGUuZC50c1wiIC8+XG5yZXF1aXJlKCdzb3VyY2UtbWFwLXN1cHBvcnQnKS5pbnN0YWxsKCk7XG5yZXF1aXJlKFwic2hvdWxkXCIpO1xuXG5pbXBvcnQgKiBhcyBSeCBmcm9tIFwicnhcIjtcbmltcG9ydCAqIGFzIEF4IGZyb20gXCIuLi9zcmMvYW5pbWF4ZVwiO1xuaW1wb3J0ICogYXMgaGVscGVyIGZyb20gXCIuLi9zcmMvaGVscGVyXCI7XG5pbXBvcnQgKiBhcyBldmVudHMgZnJvbSBcIi4uL3NyYy9ldmVudHNcIjtcbmltcG9ydCAqIGFzIFBhcmFtZXRlciBmcm9tIFwiLi4vc3JjL1BhcmFtZXRlclwiO1xuXG52YXIgYW5pbWF0b3I6IEF4LkFuaW1hdG9yID0gaGVscGVyLmdldEV4YW1wbGVBbmltYXRvcigxMDAsIDEwMCk7XG5cbi8vZWFjaCBmcmFtZSwgZmlyc3QgZHJhdyBibGFjayBiYWNrZ3JvdW5kIHRvIGVyYXNlIHRoZSBwcmV2aW91cyBjb250ZW50c1xuYW5pbWF0b3IucGxheShBeC5jcmVhdGUoKS5maWxsU3R5bGUoXCIjMDAwMDAwXCIpLmZpbGxSZWN0KFswLDBdLFsxMDAsMTAwXSkpO1xuXG4vLyB3ZSBkcmF3IHNpbmdsZSBwaXhlbHMgb2YgZGlmZmVyZW50IGh1ZXMgbW92aW5nIG9uIGEgY2lyY2xlIGNpcmN1bWZlcmVuY2VcbmFuaW1hdG9yLnBsYXkoXG4gICAgQXguY3JlYXRlKCkucGFyYWxsZWwoXG4gICAgICAgIEF4LnJhbmdlKDAsIDUpLm1hcChcbiAgICAgICAgICAgIG9mZnNldCA9PiBBeC5jcmVhdGUoKVxuICAgICAgICAgICAgICAgIC5za2V3VChvZmZzZXQgKiAwLjEpXG4gICAgICAgICAgICAgICAgLnRyYW5zbGF0ZShQYXJhbWV0ZXIucG9pbnQoXG4gICAgICAgICAgICAgICAgICAgICAgICBQYXJhbWV0ZXIuc2luKFBhcmFtZXRlci50KCkpLm1hcFZhbHVlKHggPT4gNDUgKiAoeCArIDEpKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIFBhcmFtZXRlci5jb3MoUGFyYW1ldGVyLnQoKSkubWFwVmFsdWUoeCA9PiA0NSAqICh4ICsgMSkpXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgLmZpbGxTdHlsZShcIndoaXRlXCIpLmZpbGxSZWN0KFstMSwgLTFdLCBbMywzXSlcbiAgICAgICAgKSAgICBcbiAgICApXG4pO1xuXG5oZWxwZXIucGxheUV4YW1wbGUoXCJza2V3XCIsIDIwLCBhbmltYXRvciwgMTAwLCAxMDApO1xuXG5cbmRlc2NyaWJlKCdza2V3JywgZnVuY3Rpb24gKCkge1xuICAgIGl0ICgnc2hvdWxkIG1hdGNoIHRoZSByZWZlcmVuY2UnLCBmdW5jdGlvbihkb25lKSB7XG4gICAgICAgIGhlbHBlci5zYW1lRXhhbXBsZShcInNrZXdcIiwgXCJza2V3LXJlZlwiLCBmdW5jdGlvbihlcXVhbCkge1xuICAgICAgICAgICAgZXF1YWwuc2hvdWxkLmVxdWFsKHRydWUpO1xuICAgICAgICAgICAgZG9uZSgpO1xuICAgICAgICB9KVxuICAgIH0pO1xufSk7Il0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 29 | -------------------------------------------------------------------------------- /examples/example6.ts: -------------------------------------------------------------------------------- 1 | import * as Rx from "rx"; 2 | import * as Ax from "../src/animaxe"; 3 | import * as helper from "../src/helper"; 4 | import * as events from "../src/events"; 5 | import * as Parameter from "../src/Parameter"; 6 | 7 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 8 | 9 | 10 | // todo 11 | // make add a max and min value 12 | // scale the value 13 | // make the slider impervious to rotation (mouse movement needs to be transformed by transform matrix-1 ) 14 | 15 | /** 16 | * A Button is an animation but with extra mouse state attached 17 | */ 18 | class Slider extends Ax.Operation { 19 | value: Rx.Subject; 20 | 21 | /** 22 | * @param postprocessor hook to do things like attach listeners without breaking the animation chaining 23 | */ 24 | static rectangular(value: Rx.BehaviorSubject, postprocessor ?: (Button) => void): Slider { // note Babel doesn't like this type 25 | var hotspot = Ax.create() 26 | .withinPath(Ax.create() 27 | .lineTo([ 20, 0]) 28 | .lineTo([ 20, 20]) 29 | .lineTo([ 0, 20]) 30 | .lineTo([ 0, 0]) 31 | ); 32 | 33 | 34 | value.subscribe(x => console.log("pre construction value changed", x)); 35 | 36 | var slider = new Slider( 37 | hotspot, 38 | new events.ComponentMouseState(), 39 | new events.ComponentMouseState(), 40 | value, 41 | Ax.create().fillStyle("red"), /* pressed */ 42 | Ax.create().fillStyle("orange"), /* over */ 43 | Ax.create().fillStyle("white") /* idle */ 44 | 45 | ); /* idle */ 46 | 47 | if (postprocessor) postprocessor(slider); 48 | 49 | return slider; 50 | } 51 | 52 | constructor(public hotspot: Ax.PathAnimation, // Babel doesn't like public modifier 53 | public knobMouseState: events.ComponentMouseState, // Babel doesn't like public modifier 54 | public canvasMouseState: events.ComponentMouseState, // Babel doesn't like public modifier 55 | value: Rx.Subject, 56 | onMouseDownKnob: Ax.Operation, 57 | onMouseOverKnob: Ax.Operation, 58 | onIdleKnob: Ax.Operation 59 | ) { 60 | // we build a grand animation pipeline either side of the hot spot, 61 | // then we use the total pipeline's attach function as the attach function for this animation 62 | // so the constructed Button exposes a richer API (e.g. state) than a basic animation normally wouldn't 63 | // todo slider value is not changed relatively 64 | super(Ax.create() 65 | .pipe(events.CanvasMouseEventHandler(canvasMouseState)) //global mouse listener 66 | .parallel([ 67 | Ax.create() 68 | .translate(Parameter.point(0, Parameter.updateFrom(0, value))) 69 | .if(knobMouseState.isMouseDown(), onMouseDownKnob) // Condition the animation played based on mouse state 70 | .elif(knobMouseState.isMouseOver(), onMouseOverKnob) 71 | .else(onIdleKnob) 72 | .pipe(hotspot) 73 | .pipe(events.ComponentMouseEventHandler(knobMouseState)) 74 | .fill(), 75 | Ax.create() //todo, the slider 76 | ]) 77 | .attach); 78 | knobMouseState.source = this; 79 | this.value = value; 80 | 81 | this.value.subscribe(x => console.log("slider value changed", x)); 82 | 83 | 84 | 85 | type SlideState = {eventStart: events.AxMouseEvent, valueStart: number}; 86 | 87 | // a stream of points indicating the start of the slide move, or null if a slide move is not in progress 88 | var startSlideStream: Rx.Observable = Rx.Observable.merge([ 89 | knobMouseState.mousedown 90 | .withLatestFrom(value, 91 | (evt: events.AxMouseEvent, value: number) => { 92 | return {eventStart: evt, valueStart: value} 93 | } 94 | ), 95 | canvasMouseState.mouseup.map((evt: events.AxMouseEvent) => null) 96 | ]); 97 | 98 | 99 | // a stream of number or null, indicating the new value of the slider, or null to mean no change 100 | var slideChangeStream = Rx.Observable.combineLatest( 101 | startSlideStream, 102 | canvasMouseState.mousemove, 103 | (start: SlideState, current: events.AxMouseEvent) => { 104 | return start == null ? null : current.canvasCoord[1] - start.eventStart.canvasCoord[1] + start.valueStart; 105 | } 106 | ).filter(val => val != null); 107 | 108 | // remove the nulls from the stream, and pipe into the value for the slider. 109 | slideChangeStream.tap( 110 | v => console.log("slider: value", v), 111 | err => console.error(err)) 112 | .subscribe(this.value); 113 | 114 | 115 | } 116 | } 117 | 118 | var value = new Rx.BehaviorSubject(0); 119 | 120 | //each frame, first draw black background to erase the previous contents 121 | animator.play(Ax.create().fillStyle(Parameter.rgba(Parameter.updateFrom(0, value).mapValue(x=>x*2.5), 0,0,1)).fillRect([0,0],[100,100])); 122 | 123 | animator.play(Ax.create() 124 | //.translate([40, 40]) 125 | //.rotate(Math.PI / 4) 126 | .pipe(Slider.rectangular(value)) 127 | ); 128 | 129 | helper.playExample("@name", 2, animator, 100, 100); 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /dist/examples/example1.js: -------------------------------------------------------------------------------- 1 | var Ax = require("../src/animaxe"); 2 | var helper = require("../src/helper"); 3 | var Parameter = require("../src/Parameter"); 4 | var animator = helper.getExampleAnimator(); 5 | //2 frame animated glow 6 | function spark(color) { 7 | return Ax.create() 8 | .take(1) 9 | .fillStyle(color) 10 | .fillRect([-2, -2], [5, 5]) 11 | .then(Ax.create() 12 | .take(1) 13 | .fillStyle(color) 14 | .fillRect([-1, -1], [3, 3])); 15 | } 16 | //large circle funcitons 17 | var bigSin = Parameter.sin(Parameter.t().mapValue(function (x) { return x * Math.PI * 2; })).mapValue(function (x) { return x * 40 + 50; }); 18 | var bigCos = Parameter.cos(Parameter.t().mapValue(function (x) { return x * Math.PI * 2; })).mapValue(function (x) { return x * 40 + 50; }); 19 | var red = Parameter.sin(Parameter.t().mapValue(function (x) { return x * Math.PI; })).mapValue(function (x) { return x * 125 + 125; }); 20 | var green = Parameter.sin(Parameter.t().mapValue(function (x) { return x * Math.PI; })).mapValue(function (x) { return x * 55 + 200; }); 21 | //each frame, first draw black background to erase the previous contents 22 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 23 | // move the drawing context frame of reference to the center (50,50) and then move it by a +ve x velocity, 24 | // so the frame of reference moves over time. 25 | // then draw our 2 frame spark animation in a loop so it draws forever 26 | animator.play(Ax.create() 27 | .translate([50, 50]) 28 | .velocity([50, 0]) 29 | .loop(spark("#FFFFFF"))); 30 | // move the draw context to a coordinate determined by trig (i.e. in a circle) 31 | animator.play(Ax.create() 32 | .loop(Ax.create() 33 | .translate(Parameter.point(bigSin, bigCos)) 34 | .pipe(spark(Parameter.rgba(red, green, 0, 1))))); 35 | // tween between the center (50,50) and a point on a circle. This has the effect of moving the inner spark animation 36 | // in a archimedes spiral. 37 | animator.play(Ax.create() 38 | .tween_linear([50, 50], Parameter.point(bigSin, bigCos), 1) 39 | .loop(spark("red"))); 40 | // the helper function pipes injects the context, either from a web canvas or a fake node.js one. 41 | helper.playExample("@name", 20, animator, 100, 100); 42 | 43 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImV4YW1wbGVzL2V4YW1wbGUxLnRzIl0sIm5hbWVzIjpbInNwYXJrIl0sIm1hcHBpbmdzIjoiQUFDQSxJQUFZLEVBQUUsV0FBTSxnQkFBZ0IsQ0FBQyxDQUFBO0FBQ3JDLElBQVksTUFBTSxXQUFNLGVBQWUsQ0FBQyxDQUFBO0FBRXhDLElBQVksU0FBUyxXQUFNLGtCQUFrQixDQUFDLENBQUE7QUFFOUMsSUFBSSxRQUFRLEdBQWdCLE1BQU0sQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO0FBRXhELHVCQUF1QjtBQUN2QixlQUFlLEtBQWtCO0lBQzdCQSxNQUFNQSxDQUFDQSxFQUFFQSxDQUFDQSxNQUFNQSxFQUFFQTtTQUNiQSxJQUFJQSxDQUFDQSxDQUFDQSxDQUFDQTtTQUNQQSxTQUFTQSxDQUFDQSxLQUFLQSxDQUFDQTtTQUNoQkEsUUFBUUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsRUFBRUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsRUFBRUEsQ0FBQ0EsQ0FBQ0EsRUFBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7U0FDekJBLElBQUlBLENBQUNBLEVBQUVBLENBQUNBLE1BQU1BLEVBQUVBO1NBQ1pBLElBQUlBLENBQUNBLENBQUNBLENBQUNBO1NBQ1BBLFNBQVNBLENBQUNBLEtBQUtBLENBQUNBO1NBQ2hCQSxRQUFRQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxFQUFFQSxDQUFDQSxDQUFDQSxDQUFDQSxFQUFFQSxDQUFDQSxDQUFDQSxFQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUM3QkEsQ0FBQ0E7QUFDVkEsQ0FBQ0E7QUFDRCx3QkFBd0I7QUFDeEIsSUFBSSxNQUFNLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLFVBQUEsQ0FBQyxJQUFFLE9BQUEsQ0FBQyxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFmLENBQWUsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLFVBQUEsQ0FBQyxJQUFJLE9BQUEsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQVgsQ0FBVyxDQUFDLENBQUM7QUFDbEcsSUFBSSxNQUFNLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLFVBQUEsQ0FBQyxJQUFFLE9BQUEsQ0FBQyxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFmLENBQWUsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLFVBQUEsQ0FBQyxJQUFJLE9BQUEsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQVgsQ0FBVyxDQUFDLENBQUM7QUFFbEcsSUFBSSxHQUFHLEdBQUssU0FBUyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLFVBQUEsQ0FBQyxJQUFFLE9BQUEsQ0FBQyxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQVgsQ0FBVyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsVUFBQSxDQUFDLElBQUksT0FBQSxDQUFDLEdBQUcsR0FBRyxHQUFHLEdBQUcsRUFBYixDQUFhLENBQUMsQ0FBQztBQUMvRixJQUFJLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsVUFBQSxDQUFDLElBQUUsT0FBQSxDQUFDLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBWCxDQUFXLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFBLENBQUMsSUFBSSxPQUFBLENBQUMsR0FBRyxFQUFFLEdBQUcsR0FBRyxFQUFaLENBQVksQ0FBQyxDQUFDO0FBRTlGLHdFQUF3RTtBQUN4RSxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsR0FBRyxFQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUcxRSwwR0FBMEc7QUFDMUcsNkNBQTZDO0FBQzdDLHNFQUFzRTtBQUN0RSxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUU7S0FDcEIsU0FBUyxDQUFDLENBQUMsRUFBRSxFQUFDLEVBQUUsQ0FBQyxDQUFDO0tBQ2xCLFFBQVEsQ0FBQyxDQUFDLEVBQUUsRUFBQyxDQUFDLENBQUMsQ0FBQztLQUNoQixJQUFJLENBQ0QsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUNuQixDQUNKLENBQUM7QUFFRiw4RUFBOEU7QUFDOUUsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFO0tBQ3BCLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFO0tBQ1osU0FBUyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0tBQzFDLElBQUksQ0FDRCxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUMxQyxDQUNKLENBQ0osQ0FBQztBQUVGLG9IQUFvSDtBQUNwSCwwQkFBMEI7QUFDMUIsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFO0tBQ3BCLFlBQVksQ0FBQyxDQUFDLEVBQUUsRUFBQyxFQUFFLENBQUMsRUFBRSxTQUFTLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7S0FDekQsSUFBSSxDQUNELEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FDZixDQUNKLENBQUM7QUFDRixpR0FBaUc7QUFDakcsTUFBTSxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMiLCJmaWxlIjoiZXhhbXBsZXMvZXhhbXBsZTEuanMiLCJzb3VyY2VzQ29udGVudCI6W251bGxdLCJzb3VyY2VSb290IjoiL3NvdXJjZS8ifQ== 44 | -------------------------------------------------------------------------------- /examples/arrows.ts: -------------------------------------------------------------------------------- 1 | 2 | // 'M200 150 h250 v-50 l350 100 l-350 100 v-50 h-250 z' 3 | 4 | import * as Rx from "rx"; 5 | import * as Ax from "../src/animaxe"; 6 | import * as helper from "../src/helper"; 7 | import * as events from "../src/events"; 8 | import * as Parameter from "../src/Parameter"; 9 | import * as svg from "../src/svg"; 10 | 11 | var animator: Ax.Animator = helper.getExampleAnimator(100, 100); 12 | 13 | //each frame, first draw black background to erase the previous contents 14 | animator.play(Ax.create().fillStyle("#000000").fillRect([0,0],[100,100])); 15 | 16 | 17 | 18 | // TODO move to Parameter 19 | function linearControl( 20 | initialValue: number, 21 | maxVelocity: Ax.NumberArg, 22 | targetValue: Ax.NumberArg): Parameter.Parameter { 23 | console.log("build: linearControl"); 24 | return Parameter.dt().combine( 25 | () => { 26 | var current = initialValue; 27 | return (dt: number, target: number, maxVel: number) => { 28 | var error = target - current; 29 | var velocity: number = error / dt; 30 | if (velocity > maxVel) velocity = maxVel; 31 | if (velocity < -maxVel) velocity = -maxVel; 32 | 33 | current += velocity * dt; 34 | return current; 35 | } 36 | }, 37 | Parameter.from(maxVelocity), 38 | Parameter.from(targetValue) 39 | ) 40 | } 41 | 42 | type ArrowSignal = "OPEN" | "CLOSE" | "EXIT"; 43 | 44 | class Arrow extends Ax.Operation { 45 | commands: Rx.Observer; // signals into a state machine 46 | // state: Rx.Observable; // discrete state of animation 47 | } 48 | // Simple arrow that points to (0,0) 49 | // --> 50 | function simpleArrow( 51 | stemWidth: Ax.NumberArg, stemLength: Ax.NumberArg, 52 | arrowWidth: Ax.NumberArg, arrowLength: Ax.NumberArg, 53 | speed: Ax.NumberArg, 54 | cb?: (value: Arrow) => void): Arrow { 55 | 56 | 57 | 58 | // some point we will have a tick -> command parameter 59 | var command: Parameter.Parameter = Parameter.constant("OPEN"); 60 | 61 | // The intermediate representation is a number from 0 - 1 62 | // Its current state is moved in the direction of OPEN or CLOSE 63 | // at a linear speed 64 | var commandToIR = Parameter.sin( 65 | Parameter.t().combine( 66 | () => (t: number, speed: number) => { 67 | return t * Math.PI * 2 * speed 68 | }, 69 | Parameter.from(speed) 70 | ) 71 | ).mapValue(sin => sin / 2 + 0.5); 72 | // topOuter--+ 73 | // |\ 74 | // /topLeft | \ 75 | // +-------------- \ 76 | // | +--tip 77 | // --------------+ / 78 | // /| / 79 | // botInner/ |/ 80 | 81 | 82 | 83 | 84 | var stemLengthP = Parameter.from(stemLength); 85 | var stemWidthP = Parameter.from(stemWidth); 86 | var arrowWidthP = Parameter.from(arrowWidth); 87 | var arrowLengthP = Parameter.from(arrowLength); 88 | 89 | /** 90 | * Linear interpolation 91 | */ 92 | function lerp(t: number, start: number, end: number): number { 93 | return start + t * (end - start); 94 | } 95 | 96 | 97 | var moveToTopLeftDx = commandToIR.combine( 98 | () => (t: number, stemLength: number, arrowLength: number, stemWidth: number) => { 99 | return - stemLength - arrowLength 100 | 101 | }, 102 | stemLengthP, 103 | arrowLengthP, 104 | stemWidthP 105 | ) 106 | 107 | var moveToTopLeftDy = commandToIR.combine( 108 | () => (t: number, stemLength: number, arrowLength: number, stemWidth: number) => { 109 | return -stemWidth / 2 110 | }, 111 | stemLengthP, 112 | arrowLengthP, 113 | stemWidthP 114 | ) 115 | 116 | 117 | var liveStemLength = commandToIR.combine( 118 | () => (t: number, stemLength: number, arrowLength: number) => { 119 | return lerp(t, stemLength/2, stemLength) 120 | }, 121 | stemLengthP, 122 | arrowLengthP 123 | ) 124 | 125 | 126 | var liveOverhang = commandToIR.combine( 127 | () => (t: number, stemWidth: number, arrowWidth: number) => { 128 | return arrowWidth - stemWidth 129 | }, 130 | stemWidthP, 131 | arrowWidthP 132 | ) 133 | 134 | var liveTipDx = commandToIR.combine( 135 | () => (t: number, stemWidth: number, arrowLength: number) => { 136 | return arrowLength 137 | }, 138 | stemWidthP, 139 | arrowLengthP 140 | ) 141 | 142 | var liveTipDy = commandToIR.combine( 143 | () => (t: number, stemWidth: number, arrowWidth: number) => { 144 | return arrowWidth / 2 145 | }, 146 | stemWidthP, 147 | arrowWidthP 148 | ) 149 | 150 | var path = svg.svgpath(Ax.create().beginPath(), 151 | 'M%1 %2 h%3 v-%4 l%5 %6 l-%5 %6 v-%4 h-%3 z', 152 | moveToTopLeftDx, 153 | moveToTopLeftDy, 154 | liveStemLength, 155 | liveOverhang, 156 | liveTipDx, 157 | liveTipDy 158 | ).fill(); 159 | // The zero to 1 IR affets each line on an SVG animation 160 | var arrow = new Arrow(path.attach); // todo 161 | if (cb !== undefined) cb(arrow); 162 | return arrow; 163 | } 164 | 165 | 166 | // Using http://anthonydugois.com/svg-path-builder/ 167 | 168 | animator.play( 169 | Ax.create().translate([50, 50]).fillStyle("white").pipe( 170 | simpleArrow( 171 | 3, 20, 5, 25, 172 | 2 173 | ) 174 | ) 175 | ); 176 | 177 | 178 | helper.playExample("@name", 10, animator, 100, 100); 179 | 180 | -------------------------------------------------------------------------------- /src/animaxe.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | import * as Rx from "rx"; 4 | import * as events from "./events"; 5 | import * as Parameter from "./Parameter"; 6 | import * as canvas from "./canvas"; 7 | import * as types from "./types"; 8 | import * as OT from "./frp"; 9 | export * from "./types"; 10 | 11 | 12 | 13 | export * from "./canvas"; 14 | 15 | console.log("Animaxe, https://github.com/tomlarkworthy/animaxe"); 16 | 17 | export var DEBUG = false; 18 | export var DEBUG_EVENTS = false; 19 | 20 | 21 | 22 | /** 23 | * An animation is pipeline that modifies the drawing context found in an animation Tick. Animations can be chained 24 | * together to create a more complicated Animation. They are composeable, 25 | * 26 | * e.g. ```animation1 = Ax.translate([50, 50]).fillStyle("red").fillRect([0,0], [20,20])``` 27 | * is one animation which has been formed from three subanimations. 28 | * 29 | * Animations have a lifecycle, they can be finite or infinite in length. You can start temporally compose animations 30 | * using ```anim1.then(anim2)```, which creates a new animation that plays animation 2 when animation 1 finishes. 31 | * 32 | * When an animation is sequenced into the animation pipeline. Its attach method is called which atcually builds the 33 | * RxJS pipeline. Thus an animation is not live, but really a factory for a RxJS configuration. 34 | */ 35 | export class Animator { 36 | root: Rx.Subject; 37 | t: number = 0; 38 | events: events.Events = new events.Events(); 39 | 40 | constructor(public ctx: CanvasRenderingContext2D) { 41 | this.root = new Rx.Subject() 42 | } 43 | tick(dt: number) { 44 | if (DEBUG) console.log("animator: tick", dt); 45 | var tick = new canvas.Tick(this.t, dt, this.ctx, this.events); 46 | this.t += dt; 47 | var savedTick = tick.save(); 48 | this.root.onNext(savedTick); 49 | savedTick.restore(); 50 | this.events.clear(); 51 | } 52 | ticker(dts: Rx.Observable): void { 53 | // todo this is a bit yuck 54 | dts.subscribe( 55 | this.tick.bind(this), 56 | this.root.onError.bind(this.root), 57 | this.root.onCompleted.bind(this.root) 58 | ); 59 | } 60 | 61 | play(animation: OT.SignalFn): Rx.IDisposable { 62 | var self = this; 63 | if (DEBUG) console.log("animator: play animation"); 64 | var rootWithStateRefresh = this.root.map( 65 | (tick: canvas.Tick) => { 66 | if (DEBUG) console.log("animator: ctx refresh"); 67 | return tick.restore().save(); 68 | } 69 | ); 70 | return animation 71 | .attach(rootWithStateRefresh) 72 | .subscribe(); 73 | } 74 | 75 | mousedown (x: number, y: number) { 76 | if (DEBUG_EVENTS) console.log("Animator: mousedown", x, y); 77 | this.events.mousedowns.push([x, y]); 78 | } 79 | mouseup (x: number, y: number) { 80 | if (DEBUG_EVENTS) console.log("Animator: mouseup", x, y); 81 | this.events.mouseups.push([x, y]); 82 | } 83 | onmousemove (x: number, y: number) { 84 | if (DEBUG_EVENTS) console.log("Animator: mousemoved", x, y); 85 | this.events.mousemoves.push([x, y]); 86 | } 87 | 88 | 89 | /** 90 | * Attaches listener for a canvas which will be propogated during ticks to animators that take input, e.g. UI 91 | */ 92 | registerEvents(canvas:any): void { 93 | var self = this; 94 | var rect = canvas.getBoundingClientRect(); // you have to correct for padding, todo this might get stale 95 | canvas.onmousedown = evt => self.mousedown (evt.clientX - rect.left, evt.clientY - rect.top); 96 | canvas.onmouseup = evt => self.mouseup (evt.clientX - rect.left, evt.clientY - rect.top); 97 | canvas.onmousemove = evt => self.onmousemove(evt.clientX - rect.left, evt.clientY - rect.top); 98 | } 99 | } 100 | 101 | /** 102 | * NOTE: currently fails if the streams are different lengths 103 | * @param expectedDt the expected clock tick values 104 | * @param after 105 | * @returns {Animation} 106 | */ 107 | export function assertDt(expectedDt: Rx.Observable): canvas.Operation { 108 | return new canvas.Operation(function(upstream) { 109 | return upstream.zip(expectedDt, function(tick: canvas.Tick, expectedDtValue: number) { 110 | if (tick.dt != expectedDtValue) throw new Error("unexpected dt observed: " + tick.dt + ", expected:" + expectedDtValue); 111 | return tick; 112 | }); 113 | }); 114 | } 115 | 116 | //todo would be nice if this took an iterable or some other type of simple pull stream 117 | // and used streamEquals 118 | export function assertClock(assertClock: number[]): canvas.Operation { 119 | var index = 0; 120 | 121 | return new canvas.Operation(function(upstream) { 122 | return upstream.tapOnNext(function(tick: canvas.Tick) { 123 | if (DEBUG) console.log("assertClock: ", tick); 124 | if (tick.clock < assertClock[index] - 0.00001 || tick.clock > assertClock[index] + 0.00001) { 125 | var errorMsg = "unexpected clock observed: " + tick.clock + ", expected:" + assertClock[index] 126 | console.log(errorMsg); 127 | throw new Error(errorMsg); 128 | } 129 | index ++; 130 | }); 131 | }); 132 | } 133 | 134 | export function range(min: number, max: number, step: number = 1): number[] { 135 | var n = (max - min) / step; 136 | var array = new Array(n); 137 | for (var value = 0, index = 0; value < max; value += step, index++) { 138 | array[index] = value; 139 | } 140 | types.assert(n === index) 141 | return array; 142 | } 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /dist/test/elliptical.js: -------------------------------------------------------------------------------- 1 | // THIS IS AUTO GENERATED TEST CODE, DO NOT MODIFY DIRECTLY 2 | /// 3 | /// 4 | /// 5 | require('source-map-support').install(); 6 | require("should"); 7 | var Ax = require("../src/animaxe"); 8 | var helper = require("../src/helper"); 9 | var svg = require("../src/svg"); 10 | var animator = helper.getExampleAnimator(100, 100); 11 | var cases = [ 12 | /* 13 | 'M10 20 A15 15 0 0 0 30 20', // checked against SVG behaviour 14 | 'M40 20 A15 15 0 0 1 60 20', // checked against SVG behaviour 15 | 'M70 20 A15 15 0 1 0 90 20', // checked against SVG behaviour 16 | 'M10 50 A15 15 0 1 1 30 50', // checked against SVG behaviour 17 | 18 | 'M50 40 A15 15 0 0 0 50 60', // checked against SVG behaviour 19 | 'M80 40 A15 15 0 0 1 80 60', // checked against SVG behaviour 20 | 'M20 70 A15 15 0 1 0 20 90', // checked against SVG behaviour 21 | 'M50 70 A15 15 0 1 1 50 90', // checked against SVG behaviour 22 | */ 23 | 'M80 70 A5 5 0 1 1 80 90', 24 | ]; 25 | for (var t = 0; t < Math.PI * 2; t += 0.1) { 26 | var x = Math.sin(t) * 15 + 50; 27 | var y = Math.cos(t) * 15 + 50; 28 | } 29 | for (var i = 0; i < cases.length; i++) { 30 | animator.play(svg.svgpath(Ax.create().beginPath().strokeStyle("red"), cases[i]).stroke()); 31 | } 32 | helper.playExample("elliptical", 1, animator, 100, 100); 33 | describe('elliptical', function () { 34 | it('should match the reference', function (done) { 35 | helper.sameExample("elliptical", "elliptical-ref", function (equal) { 36 | equal.should.equal(true); 37 | done(); 38 | }); 39 | }); 40 | }); 41 | 42 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImV4YW1wbGUudGVtcGxhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkRBQTJEO0FBQzNELDZDQUE2QztBQUM3Qyw0Q0FBNEM7QUFDNUMsMkNBQTJDO0FBQzNDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ3hDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUlsQixJQUFZLEVBQUUsV0FBTSxnQkFBZ0IsQ0FBQyxDQUFBO0FBQ3JDLElBQVksTUFBTSxXQUFNLGVBQWUsQ0FBQyxDQUFBO0FBR3hDLElBQVksR0FBRyxXQUFNLFlBQVksQ0FBQyxDQUFBO0FBRWxDLElBQUksUUFBUSxHQUFnQixNQUFNLENBQUMsa0JBQWtCLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBRWhFLElBQUksS0FBSyxHQUFHO0lBQ1I7Ozs7Ozs7Ozs7TUFVRTtJQUNGLHlCQUF5QjtDQUM1QixDQUFDO0FBRUYsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFHLENBQUMsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUN6QyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7SUFDOUIsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDO0FBR2xDLENBQUM7QUFDRCxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO0lBQ3BDLFFBQVEsQ0FBQyxJQUFJLENBQ1QsR0FBRyxDQUFDLE9BQU8sQ0FDUCxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxFQUFFLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUMxQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQ1gsQ0FBQyxNQUFNLEVBQUUsQ0FDYixDQUFDO0FBQ04sQ0FBQztBQUdELE1BQU0sQ0FBQyxXQUFXLENBQUMsWUFBWSxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBRXhELFFBQVEsQ0FBQyxZQUFZLEVBQUU7SUFDbkIsRUFBRSxDQUFFLDRCQUE0QixFQUFFLFVBQVMsSUFBSTtRQUMzQyxNQUFNLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxVQUFTLEtBQUs7WUFDN0QsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDekIsSUFBSSxFQUFFLENBQUM7UUFDWCxDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDLENBQUMiLCJmaWxlIjoiZGlzdC90ZXN0L2VsbGlwdGljYWwuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBUSElTIElTIEFVVE8gR0VORVJBVEVEIFRFU1QgQ09ERSwgRE8gTk9UIE1PRElGWSBESVJFQ1RMWVxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL3Nob3VsZC5kLnRzXCIgLz5cbi8vLyA8cmVmZXJlbmNlIHBhdGg9XCIuLi90eXBlcy9tb2NoYS5kLnRzXCIgLz5cbi8vLyA8cmVmZXJlbmNlIHBhdGg9XCIuLi90eXBlcy9ub2RlLmQudHNcIiAvPlxucmVxdWlyZSgnc291cmNlLW1hcC1zdXBwb3J0JykuaW5zdGFsbCgpO1xucmVxdWlyZShcInNob3VsZFwiKTtcblxuXG5pbXBvcnQgKiBhcyBSeCBmcm9tIFwicnhcIjtcbmltcG9ydCAqIGFzIEF4IGZyb20gXCIuLi9zcmMvYW5pbWF4ZVwiO1xuaW1wb3J0ICogYXMgaGVscGVyIGZyb20gXCIuLi9zcmMvaGVscGVyXCI7XG5pbXBvcnQgKiBhcyBldmVudHMgZnJvbSBcIi4uL3NyYy9ldmVudHNcIjtcbmltcG9ydCAqIGFzIFBhcmFtZXRlciBmcm9tIFwiLi4vc3JjL1BhcmFtZXRlclwiO1xuaW1wb3J0ICogYXMgc3ZnIGZyb20gXCIuLi9zcmMvc3ZnXCI7XG5cbnZhciBhbmltYXRvcjogQXguQW5pbWF0b3IgPSBoZWxwZXIuZ2V0RXhhbXBsZUFuaW1hdG9yKDEwMCwgMTAwKTtcblxudmFyIGNhc2VzID0gW1xuICAgIC8qXG4gICAgJ00xMCAyMCBBMTUgMTUgMCAwIDAgMzAgMjAnLCAvLyBjaGVja2VkIGFnYWluc3QgU1ZHIGJlaGF2aW91clxuICAgICdNNDAgMjAgQTE1IDE1IDAgMCAxIDYwIDIwJywgLy8gY2hlY2tlZCBhZ2FpbnN0IFNWRyBiZWhhdmlvdXJcbiAgICAnTTcwIDIwIEExNSAxNSAwIDEgMCA5MCAyMCcsIC8vIGNoZWNrZWQgYWdhaW5zdCBTVkcgYmVoYXZpb3VyXG4gICAgJ00xMCA1MCBBMTUgMTUgMCAxIDEgMzAgNTAnLCAvLyBjaGVja2VkIGFnYWluc3QgU1ZHIGJlaGF2aW91clxuICAgIFxuICAgICdNNTAgNDAgQTE1IDE1IDAgMCAwIDUwIDYwJywgLy8gY2hlY2tlZCBhZ2FpbnN0IFNWRyBiZWhhdmlvdXJcbiAgICAnTTgwIDQwIEExNSAxNSAwIDAgMSA4MCA2MCcsIC8vIGNoZWNrZWQgYWdhaW5zdCBTVkcgYmVoYXZpb3VyXG4gICAgJ00yMCA3MCBBMTUgMTUgMCAxIDAgMjAgOTAnLCAvLyBjaGVja2VkIGFnYWluc3QgU1ZHIGJlaGF2aW91clxuICAgICdNNTAgNzAgQTE1IDE1IDAgMSAxIDUwIDkwJywgLy8gY2hlY2tlZCBhZ2FpbnN0IFNWRyBiZWhhdmlvdXJcbiAgICAqL1xuICAgICdNODAgNzAgQTUgNSAwIDEgMSA4MCA5MCcsXG5dO1xuXG5mb3IgKHZhciB0ID0gMDsgdCA8IE1hdGguUEkgKiAyIDsgdCArPSAwLjEpIHtcbiAgICB2YXIgeCA9IE1hdGguc2luKHQpICogMTUgKyA1MDtcbiAgICB2YXIgeSA9IE1hdGguY29zKHQpICogMTUgKyA1MDtcbiAgICBcbiAgICAvLyBjYXNlcy5wdXNoKCdNNTAgNTAgQTUgNSAwIDEgMSAnICsgeCArICcgJyArIHkpO1xufVxuZm9yICh2YXIgaSA9IDA7IGkgPCBjYXNlcy5sZW5ndGg7IGkrKykge1xuICAgIGFuaW1hdG9yLnBsYXkoXG4gICAgICAgIHN2Zy5zdmdwYXRoKFxuICAgICAgICAgICAgQXguY3JlYXRlKCkuYmVnaW5QYXRoKCkuc3Ryb2tlU3R5bGUoXCJyZWRcIiksXG4gICAgICAgICAgICBjYXNlc1tpXVxuICAgICAgICApLnN0cm9rZSgpXG4gICAgKTsgICBcbn1cblxuIFxuaGVscGVyLnBsYXlFeGFtcGxlKFwiZWxsaXB0aWNhbFwiLCAxLCBhbmltYXRvciwgMTAwLCAxMDApO1xuXG5kZXNjcmliZSgnZWxsaXB0aWNhbCcsIGZ1bmN0aW9uICgpIHtcbiAgICBpdCAoJ3Nob3VsZCBtYXRjaCB0aGUgcmVmZXJlbmNlJywgZnVuY3Rpb24oZG9uZSkge1xuICAgICAgICBoZWxwZXIuc2FtZUV4YW1wbGUoXCJlbGxpcHRpY2FsXCIsIFwiZWxsaXB0aWNhbC1yZWZcIiwgZnVuY3Rpb24oZXF1YWwpIHtcbiAgICAgICAgICAgIGVxdWFsLnNob3VsZC5lcXVhbCh0cnVlKTtcbiAgICAgICAgICAgIGRvbmUoKTtcbiAgICAgICAgfSlcbiAgICB9KTtcbn0pOyJdLCJzb3VyY2VSb290IjoiL3NvdXJjZS8ifQ== 43 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var ts = require('gulp-typescript'); 3 | var merge = require('merge2'); 4 | var mocha = require('gulp-mocha'); 5 | var gutil = require('gulp-util'); 6 | var replace = require('gulp-replace'); 7 | var template = require('gulp-template'); 8 | var rename = require('gulp-rename'); 9 | var peg = require('gulp-peg'); 10 | var sourcemaps = require('gulp-sourcemaps'); 11 | var transform = require('vinyl-transform'); 12 | var del = require('del'); 13 | var fs = require('fs'); 14 | var tslint = require('gulp-tslint'); 15 | var webpack = require('webpack'); 16 | var typedoc = require("gulp-typedoc"); 17 | var extractExampleCode = require("./scripts/extractExampleCode"); 18 | var ignore = new webpack.IgnorePlugin(new RegExp("^(canvas|mongoose|react)$")); 19 | var livereload = require('gulp-livereload'); 20 | 21 | var npm_info = JSON.parse(fs.readFileSync("package.json").toString()); 22 | 23 | var examples = fs.readdirSync("examples").map(function(filename) { 24 | return filename.replace(".ts", ""); 25 | }); 26 | 27 | 28 | gulp.task('clean', function(cb) { 29 | del([ 30 | 'dist', 31 | 'compiled', 32 | 'test/*.js', 33 | 'test/*.js.map', 34 | 'examples/*.js', 35 | 'examples/*.js.map', 36 | 'src/*.js', 37 | 'src/*.js.map' 38 | ], cb); 39 | }); 40 | 41 | gulp.task ("peg:svg", function(cb) { 42 | gulp 43 | .src( "peg/svg.peg" ) 44 | .pipe( peg( ).on( "error", gutil.log ) ) 45 | .pipe( gulp.dest( "dist/peg" ) ) // for mocha tests 46 | .pipe( gulp.dest( "peg" ) ) // for html examples 47 | 48 | }); 49 | 50 | gulp.task('compile', function() { 51 | var tsResult = gulp.src(['./src/*.ts', './examples/example1*.ts']) 52 | .pipe(sourcemaps.init()) 53 | .pipe(ts('tsconfig.json')); 54 | return merge([ // Merge the two output streams, so this task is finished when the IO of both operations are done. 55 | tsResult.dts.pipe(gulp.dest('./dist')), 56 | tsResult.js 57 | .pipe(sourcemaps.write({sourceMappingURLPrefix: "../"})) 58 | .pipe(gulp.dest('./dist')) 59 | ]); 60 | }); 61 | 62 | var projects = {}; // each example has a set of ts projects to enable continuous compilation 63 | 64 | /** 65 | * Wrap files in the examples directory, with html-, test- and watch- tasks. 66 | * html- task generates a webpage with the example embedded in it. 67 | * test- task generates a mocha test around the example that ensure the images produced match a reference image. 68 | * watch- task watches the source code and live reloads the embedding webpage. 69 | */ 70 | function createExampleTasksFor(exampleName) { 71 | var exampleNameJS = exampleName + '.js'; 72 | var exampleNameTS = exampleName + '.ts'; 73 | 74 | gulp.task('html-' + exampleName, ['compile'], function() { 75 | gulp.src('html/example.template.html') 76 | .pipe(rename("html/" + exampleName + ".html")) 77 | .pipe(template({name: exampleName})) 78 | .pipe(gulp.dest('.')) 79 | .pipe(livereload()) 80 | }); 81 | 82 | gulp.task('test-' + exampleName, ['compile'], function() { 83 | // we take the example source code 84 | var content = fs.readFileSync('examples/' + exampleNameTS).toString(); 85 | content = content 86 | .replace("@name", exampleName); 87 | 88 | return gulp.src('test/example.template.ts') 89 | .pipe(template({name: exampleName, content: content})) // pass it through a template 90 | .pipe(sourcemaps.init()) 91 | .pipe(ts('tsconfig.json')) // compile it 92 | .js 93 | .pipe(rename("dist/test/" + exampleNameJS))//rename the js, and align with normal compile target 94 | .pipe(sourcemaps.write()) 95 | .pipe(gulp.dest("."))// we need a real copy of it to satisfy mocha 96 | .pipe(mocha({ reporter: 'list' })); // run it with mocha*/ 97 | }); 98 | 99 | gulp.task('watch-' + exampleName, ['html-' + exampleName], function() { 100 | // we take the example source code 101 | livereload.listen(); 102 | gulp.watch(['src/*.ts', 'examples/' + exampleNameTS], ['html-' + exampleName]); 103 | }); 104 | } 105 | 106 | // run all the tests (populating the dist/test directory with source) 107 | gulp.task("tests", examples.map(function(name) { return "test-" + name}) ) 108 | gulp.task("htmls", examples.map(function(name) { return "html-" + name}) ) 109 | 110 | // create various tasks on a per example basis 111 | for (var i = 0; i < examples.length; i++ ) { // (counting from 1) 112 | createExampleTasksFor(examples[i]); 113 | } 114 | 115 | 116 | gulp.task("doc", function() { 117 | return gulp 118 | .src(["src/*.ts"]) 119 | .pipe(typedoc({ 120 | module: "commonjs", 121 | target: "es5", 122 | out: "docs/", 123 | name: "Animaxe" 124 | })) 125 | ; 126 | }); 127 | 128 | 129 | gulp.task("deploy", [/*"tests"*/"htmls"/*TODO: (not working) "doc"*/], function() { 130 | console.log("deploying"); 131 | // console.log(npm_info); 132 | var imgs = gulp.src('./images/**').pipe(gulp.dest("/Users/larkworthy/dev/animaxe-web/site/master/images")); 133 | var lib_dist = gulp.src('./dist/**').pipe(gulp.dest("/Users/larkworthy/dev/animaxe-web/site/libs")); 134 | var src = gulp.src('./src/**').pipe(gulp.dest("/Users/larkworthy/dev/animaxe-web/site/src")); 135 | var examples = gulp.src('./examples/**').pipe(gulp.dest("/Users/larkworthy/dev/animaxe-web/site/examples")); 136 | var html = gulp.src('./html/**').pipe(gulp.dest("/Users/larkworthy/dev/animaxe-web/site/html")); 137 | var docs = gulp.src('./docs/**').pipe(gulp.dest("/Users/larkworthy/dev/animaxe-web/site")); 138 | var modules = gulp.src('./node_modules/**').pipe(gulp.dest("/Users/larkworthy/dev/animaxe-web/site/node_modules")); 139 | 140 | return merge([imgs, lib_dist, src, examples, html, docs, modules]); 141 | }); 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /dist/src/parametric.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines utilities for working with parametric function, e.g. x = sin(t), y = cos(t) where t = 0 ... 10 3 | */ 4 | function evaluate(equations, t) { 5 | return equations.map(function (fn) { return fn(t); }); 6 | } 7 | function midpoint(a, b) { 8 | return a.map(function (v, index) { return (v + b[index]) / 2; }); 9 | } 10 | function add(a, b) { 11 | return a + b; 12 | } 13 | function distance2(a, b) { 14 | return a.map(function (v, index) { return (v - b[index]) * (v - b[index]); }).reduce(add, 0); 15 | } 16 | function trace(equations, t_min, t_max, tolerance_px2, minimum_splits, maximum_splits, t_min_value, t_max_value) { 17 | // console.log(minimum_splits, maximum_splits); 18 | if (tolerance_px2 === void 0) { tolerance_px2 = 1; } 19 | if (minimum_splits === void 0) { minimum_splits = 4; } 20 | if (maximum_splits === void 0) { maximum_splits = 100; } 21 | // figure out the start and end point if not provided 22 | var min = t_min_value || evaluate(equations, t_min); 23 | var max = t_max_value || evaluate(equations, t_max); 24 | var t_mid = (t_min + t_max) / 2; 25 | var mid_predict = midpoint(min, max); // guess the mid point based on linear interpolation 26 | var mid_actual = evaluate(equations, t_mid); // get the real midpoint from the equations 27 | // the distance between predicted and actuall is our error term 28 | var dist2 = distance2(mid_predict, mid_actual); 29 | var result = []; 30 | // if the caller did not specify the min_value, they will want to see it in the result set 31 | if (!t_min_value) 32 | result.push({ point: min, t: t_min }); 33 | if ((dist2 < tolerance_px2 && minimum_splits <= 0) || maximum_splits <= 0) { 34 | } 35 | else { 36 | // high error, 37 | // we recurse by dividing the problem into 2 smaller tracing problems 38 | minimum_splits = Math.ceil((minimum_splits - 1) / 2); 39 | maximum_splits = Math.ceil((maximum_splits - 1) / 2); 40 | result = result.concat(trace(equations, t_min, t_mid, tolerance_px2, minimum_splits, maximum_splits, min, mid_actual)); 41 | result.push({ point: mid_actual, t: t_mid }); 42 | result = result.concat(trace(equations, t_mid, t_max, tolerance_px2, minimum_splits, maximum_splits, mid_actual, max)); 43 | } 44 | // if the caller did not specify the max_value, they will want to see it in the result set 45 | if (!t_max_value) 46 | result.push({ point: max, t: t_max }); 47 | // console.log(result); 48 | return result; 49 | } 50 | exports.trace = trace; 51 | 52 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9wYXJhbWV0cmljLnRzIl0sIm5hbWVzIjpbImV2YWx1YXRlIiwibWlkcG9pbnQiLCJhZGQiLCJkaXN0YW5jZTIiLCJ0cmFjZSJdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxrQkFBa0IsU0FBb0MsRUFBRSxDQUFTO0lBQzdEQSxNQUFNQSxDQUFDQSxTQUFTQSxDQUFDQSxHQUFHQSxDQUFDQSxVQUFBQSxFQUFFQSxJQUFJQSxPQUFBQSxFQUFFQSxDQUFDQSxDQUFDQSxDQUFDQSxFQUFMQSxDQUFLQSxDQUFDQSxDQUFDQTtBQUN0Q0EsQ0FBQ0E7QUFDRCxrQkFBa0IsQ0FBVyxFQUFFLENBQVc7SUFDdENDLE1BQU1BLENBQUNBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLFVBQUNBLENBQUNBLEVBQUVBLEtBQUtBLElBQUtBLE9BQUFBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBLEtBQUtBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLEVBQWxCQSxDQUFrQkEsQ0FBQ0EsQ0FBQ0E7QUFDbkRBLENBQUNBO0FBQ0QsYUFBYSxDQUFTLEVBQUUsQ0FBUztJQUM3QkMsTUFBTUEsQ0FBQ0EsQ0FBQ0EsR0FBR0EsQ0FBQ0EsQ0FBQ0E7QUFDakJBLENBQUNBO0FBQ0QsbUJBQW1CLENBQVcsRUFBRSxDQUFXO0lBQ3ZDQyxNQUFNQSxDQUFDQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxVQUFDQSxDQUFDQSxFQUFFQSxLQUFLQSxJQUFLQSxPQUFBQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxLQUFLQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxLQUFLQSxDQUFDQSxDQUFDQSxFQUEvQkEsQ0FBK0JBLENBQUNBLENBQUNBLE1BQU1BLENBQUNBLEdBQUdBLEVBQUVBLENBQUNBLENBQUNBLENBQUNBO0FBQy9FQSxDQUFDQTtBQUVELGVBQ0ksU0FBb0MsRUFDcEMsS0FBYSxFQUNiLEtBQWEsRUFDYixhQUF5QixFQUN6QixjQUFrQixFQUNsQixjQUFvQixFQUNwQixXQUF1QixFQUN2QixXQUF1QjtJQUd2QkMsK0NBQStDQTtJQVAvQ0EsNkJBQXlCQSxHQUF6QkEsaUJBQXlCQTtJQUN6QkEsOEJBQWtCQSxHQUFsQkEsa0JBQWtCQTtJQUNsQkEsOEJBQW9CQSxHQUFwQkEsb0JBQW9CQTtJQU9wQkEscURBQXFEQTtJQUNyREEsSUFBSUEsR0FBR0EsR0FBR0EsV0FBV0EsSUFBSUEsUUFBUUEsQ0FBQ0EsU0FBU0EsRUFBRUEsS0FBS0EsQ0FBQ0EsQ0FBQ0E7SUFDcERBLElBQUlBLEdBQUdBLEdBQUdBLFdBQVdBLElBQUlBLFFBQVFBLENBQUNBLFNBQVNBLEVBQUVBLEtBQUtBLENBQUNBLENBQUNBO0lBRXBEQSxJQUFJQSxLQUFLQSxHQUFHQSxDQUFDQSxLQUFLQSxHQUFHQSxLQUFLQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQTtJQUNoQ0EsSUFBSUEsV0FBV0EsR0FBR0EsUUFBUUEsQ0FBQ0EsR0FBR0EsRUFBRUEsR0FBR0EsQ0FBQ0EsQ0FBQ0EsQ0FBRUEsb0RBQW9EQTtJQUMzRkEsSUFBSUEsVUFBVUEsR0FBR0EsUUFBUUEsQ0FBQ0EsU0FBU0EsRUFBRUEsS0FBS0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsMkNBQTJDQTtJQUV4RkEsK0RBQStEQTtJQUMvREEsSUFBSUEsS0FBS0EsR0FBR0EsU0FBU0EsQ0FBQ0EsV0FBV0EsRUFBRUEsVUFBVUEsQ0FBQ0EsQ0FBQUE7SUFFOUNBLElBQUlBLE1BQU1BLEdBQUdBLEVBQUVBLENBQUNBO0lBQ2hCQSwwRkFBMEZBO0lBQzFGQSxFQUFFQSxDQUFDQSxDQUFDQSxDQUFDQSxXQUFXQSxDQUFDQTtRQUFDQSxNQUFNQSxDQUFDQSxJQUFJQSxDQUFDQSxFQUFDQSxLQUFLQSxFQUFFQSxHQUFHQSxFQUFFQSxDQUFDQSxFQUFFQSxLQUFLQSxFQUFDQSxDQUFDQSxDQUFDQTtJQUN0REEsRUFBRUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsS0FBS0EsR0FBR0EsYUFBYUEsSUFBSUEsY0FBY0EsSUFBSUEsQ0FBQ0EsQ0FBQ0EsSUFBSUEsY0FBY0EsSUFBSUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7SUFHNUVBLENBQUNBO0lBQUNBLElBQUlBLENBQUNBLENBQUNBO1FBQ0pBLGNBQWNBO1FBQ2RBLHFFQUFxRUE7UUFDckVBLGNBQWNBLEdBQUdBLElBQUlBLENBQUNBLElBQUlBLENBQUNBLENBQUNBLGNBQWNBLEdBQUdBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBLENBQUFBO1FBQ3BEQSxjQUFjQSxHQUFHQSxJQUFJQSxDQUFDQSxJQUFJQSxDQUFDQSxDQUFDQSxjQUFjQSxHQUFHQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxDQUFBQTtRQUNwREEsTUFBTUEsR0FBR0EsTUFBTUEsQ0FBQ0EsTUFBTUEsQ0FBQ0EsS0FBS0EsQ0FBQ0EsU0FBU0EsRUFBRUEsS0FBS0EsRUFBRUEsS0FBS0EsRUFBRUEsYUFBYUEsRUFBRUEsY0FBY0EsRUFBRUEsY0FBY0EsRUFBRUEsR0FBR0EsRUFBRUEsVUFBVUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7UUFDdkhBLE1BQU1BLENBQUNBLElBQUlBLENBQUNBLEVBQUNBLEtBQUtBLEVBQUVBLFVBQVVBLEVBQUVBLENBQUNBLEVBQUVBLEtBQUtBLEVBQUNBLENBQUNBLENBQUNBO1FBQzNDQSxNQUFNQSxHQUFHQSxNQUFNQSxDQUFDQSxNQUFNQSxDQUFDQSxLQUFLQSxDQUFDQSxTQUFTQSxFQUFFQSxLQUFLQSxFQUFFQSxLQUFLQSxFQUFFQSxhQUFhQSxFQUFFQSxjQUFjQSxFQUFFQSxjQUFjQSxFQUFFQSxVQUFVQSxFQUFFQSxHQUFHQSxDQUFDQSxDQUFDQSxDQUFDQTtJQUMzSEEsQ0FBQ0E7SUFDREEsMEZBQTBGQTtJQUMxRkEsRUFBRUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsV0FBV0EsQ0FBQ0E7UUFBQ0EsTUFBTUEsQ0FBQ0EsSUFBSUEsQ0FBQ0EsRUFBQ0EsS0FBS0EsRUFBRUEsR0FBR0EsRUFBRUEsQ0FBQ0EsRUFBRUEsS0FBS0EsRUFBQ0EsQ0FBQ0EsQ0FBQ0E7SUFFdERBLHVCQUF1QkE7SUFDdkJBLE1BQU1BLENBQUNBLE1BQU1BLENBQUNBO0FBQ2xCQSxDQUFDQTtBQTVDZSxhQUFLLFFBNENwQixDQUFBIiwiZmlsZSI6InNyYy9wYXJhbWV0cmljLmpzIiwic291cmNlc0NvbnRlbnQiOltudWxsXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= 53 | -------------------------------------------------------------------------------- /dist/test/svg.js: -------------------------------------------------------------------------------- 1 | // THIS IS AUTO GENERATED TEST CODE, DO NOT MODIFY DIRECTLY 2 | /// 3 | /// 4 | /// 5 | require('source-map-support').install(); 6 | require("should"); 7 | var Ax = require("../src/animaxe"); 8 | var helper = require("../src/helper"); 9 | var Parameter = require("../src/Parameter"); 10 | var svg = require("../src/svg"); 11 | var animator = helper.getExampleAnimator(100, 100); 12 | //each frame, first draw black background to erase the previous contents 13 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 14 | animator.play(svg.svgpath(Ax.create().beginPath().strokeStyle("blue"), 'M3,7 5-6 L1,7 1e2-.4 m-10,10 l10,0 ' + 15 | 'V27 89 H23 v10 h10 ' + 16 | 'C33,43 38,47 43,47 c0,5 5,10 10,10 ' + 17 | // 'S63,67 63,67 s-10,10 10,10 ' + // smooth curve to 18 | // 'Q50,50 73,57 q20,-5 0,-10 ' + // NaN quadratic curve :/ 19 | // 'T70,40 t0,-15 ' + // smooth quadratic curve to 20 | 'A5,5 45 1,0 40,20 a5,5 20 0,1 -10-10 z').stroke()); 21 | // Using http://anthonydugois.com/svg-path-builder/ 22 | animator.play(svg.svgpath(Ax.create().beginPath().strokeStyle("yellow").lineWidth(20).scale([0.1, 0.1]), 'M350 300 a50 50 0 1 0 -200 0 c0 100 200 0 200 100 a50 50 0 1 1 -200 0 ' + 23 | 'M400 250 V%1 L500 500 L600 400 V250 ' + 24 | 'M850 300 A50 50 0 1 0 650 300 V400 A50 50 0 1 0 850 400 V350 H750', Parameter.cos(Parameter.t()).mapValue(function (x) { return x * 400; })).stroke()); 25 | helper.playExample("svg", 10, animator, 100, 100); 26 | describe('svg', function () { 27 | it('should match the reference', function (done) { 28 | helper.sameExample("svg", "svg-ref", function (equal) { 29 | equal.should.equal(true); 30 | done(); 31 | }); 32 | }); 33 | }); 34 | 35 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImV4YW1wbGUudGVtcGxhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkRBQTJEO0FBQzNELDZDQUE2QztBQUM3Qyw0Q0FBNEM7QUFDNUMsMkNBQTJDO0FBQzNDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ3hDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUdsQixJQUFZLEVBQUUsV0FBTSxnQkFBZ0IsQ0FBQyxDQUFBO0FBQ3JDLElBQVksTUFBTSxXQUFNLGVBQWUsQ0FBQyxDQUFBO0FBRXhDLElBQVksU0FBUyxXQUFNLGtCQUFrQixDQUFDLENBQUE7QUFDOUMsSUFBWSxHQUFHLFdBQU0sWUFBWSxDQUFDLENBQUE7QUFFbEMsSUFBSSxRQUFRLEdBQWdCLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFFaEUsd0VBQXdFO0FBQ3hFLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxHQUFHLEVBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRTFFLFFBQVEsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxFQUFFLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxFQUNuRSxzQ0FBc0M7SUFDdEMsMkNBQTJDO0lBQzNDLDJDQUEyQztJQUM3QyxxRUFBcUU7SUFDckUsNEVBQTRFO0lBQzVFLCtFQUErRTtJQUM3RSw0Q0FBNEMsQ0FDM0MsQ0FBQyxNQUFNLEVBQUUsQ0FDWCxDQUFDO0FBSUYsbURBQW1EO0FBRW5ELFFBQVEsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxFQUFFLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsRUFDckcsd0VBQXdFO0lBQ3hFLHNDQUFzQztJQUN0QyxtRUFBbUUsRUFBRSxTQUFTLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFBLENBQUMsSUFBSSxPQUFBLENBQUMsR0FBRyxHQUFHLEVBQVAsQ0FBTyxDQUFDLENBQ3ZILENBQUMsTUFBTSxFQUFFLENBQ1gsQ0FBQztBQUdGLE1BQU0sQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBRWxELFFBQVEsQ0FBQyxLQUFLLEVBQUU7SUFDWixFQUFFLENBQUUsNEJBQTRCLEVBQUUsVUFBUyxJQUFJO1FBQzNDLE1BQU0sQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLFNBQVMsRUFBRSxVQUFTLEtBQUs7WUFDL0MsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDekIsSUFBSSxFQUFFLENBQUM7UUFDWCxDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDLENBQUMiLCJmaWxlIjoiZGlzdC90ZXN0L3N2Zy5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFRISVMgSVMgQVVUTyBHRU5FUkFURUQgVEVTVCBDT0RFLCBETyBOT1QgTU9ESUZZIERJUkVDVExZXG4vLy8gPHJlZmVyZW5jZSBwYXRoPVwiLi4vdHlwZXMvc2hvdWxkLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL21vY2hhLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL25vZGUuZC50c1wiIC8+XG5yZXF1aXJlKCdzb3VyY2UtbWFwLXN1cHBvcnQnKS5pbnN0YWxsKCk7XG5yZXF1aXJlKFwic2hvdWxkXCIpO1xuXG5pbXBvcnQgKiBhcyBSeCBmcm9tIFwicnhcIjtcbmltcG9ydCAqIGFzIEF4IGZyb20gXCIuLi9zcmMvYW5pbWF4ZVwiO1xuaW1wb3J0ICogYXMgaGVscGVyIGZyb20gXCIuLi9zcmMvaGVscGVyXCI7XG5pbXBvcnQgKiBhcyBldmVudHMgZnJvbSBcIi4uL3NyYy9ldmVudHNcIjtcbmltcG9ydCAqIGFzIFBhcmFtZXRlciBmcm9tIFwiLi4vc3JjL1BhcmFtZXRlclwiO1xuaW1wb3J0ICogYXMgc3ZnIGZyb20gXCIuLi9zcmMvc3ZnXCI7XG5cbnZhciBhbmltYXRvcjogQXguQW5pbWF0b3IgPSBoZWxwZXIuZ2V0RXhhbXBsZUFuaW1hdG9yKDEwMCwgMTAwKTtcblxuLy9lYWNoIGZyYW1lLCBmaXJzdCBkcmF3IGJsYWNrIGJhY2tncm91bmQgdG8gZXJhc2UgdGhlIHByZXZpb3VzIGNvbnRlbnRzXG5hbmltYXRvci5wbGF5KEF4LmNyZWF0ZSgpLmZpbGxTdHlsZShcIiMwMDAwMDBcIikuZmlsbFJlY3QoWzAsMF0sWzEwMCwxMDBdKSk7XG5cbmFuaW1hdG9yLnBsYXkoc3ZnLnN2Z3BhdGgoQXguY3JlYXRlKCkuYmVnaW5QYXRoKCkuc3Ryb2tlU3R5bGUoXCJibHVlXCIpLFxuICAnTTMsNyA1LTYgTDEsNyAxZTItLjQgbS0xMCwxMCBsMTAsMCAgJyArXG4gICdWMjcgODkgSDIzICAgICAgICAgICB2MTAgaDEwICAgICAgICAgICAgICcgK1xuICAnQzMzLDQzIDM4LDQ3IDQzLDQ3ICAgYzAsNSA1LDEwIDEwLDEwICAgICAnICtcbi8vICAnUzYzLDY3IDYzLDY3ICAgICAgICAgcy0xMCwxMCAxMCwxMCAgICAgICAnICsgIC8vIHNtb290aCBjdXJ2ZSB0b1xuLy8gICdRNTAsNTAgNzMsNTcgICAgICAgICBxMjAsLTUgMCwtMTAgICAgICAgICcgKyAgLy8gTmFOIHF1YWRyYXRpYyBjdXJ2ZSA6L1xuLy8gICdUNzAsNDAgICAgICAgICAgICAgICB0MCwtMTUgICAgICAgICAgICAgICcgKyAgLy8gc21vb3RoIHF1YWRyYXRpYyBjdXJ2ZSB0b1xuICAnQTUsNSA0NSAxLDAgNDAsMjAgICAgYTUsNSAyMCAwLDEgLTEwLTEwICB6J1xuICApLnN0cm9rZSgpXG4pO1xuXG5cblxuLy8gVXNpbmcgaHR0cDovL2FudGhvbnlkdWdvaXMuY29tL3N2Zy1wYXRoLWJ1aWxkZXIvXG5cbmFuaW1hdG9yLnBsYXkoc3ZnLnN2Z3BhdGgoQXguY3JlYXRlKCkuYmVnaW5QYXRoKCkuc3Ryb2tlU3R5bGUoXCJ5ZWxsb3dcIikubGluZVdpZHRoKDIwKS5zY2FsZShbMC4xLCAwLjFdKSxcbiAgJ00zNTAgMzAwIGE1MCA1MCAwIDEgMCAtMjAwIDAgYzAgMTAwIDIwMCAwIDIwMCAxMDAgYTUwIDUwIDAgMSAxIC0yMDAgMCAnICsgXG4gICdNNDAwIDI1MCBWJTEgTDUwMCA1MDAgTDYwMCA0MDAgVjI1MCAnICtcbiAgJ004NTAgMzAwIEE1MCA1MCAwIDEgMCA2NTAgMzAwIFY0MDAgQTUwIDUwIDAgMSAwIDg1MCA0MDAgVjM1MCBINzUwJywgUGFyYW1ldGVyLmNvcyhQYXJhbWV0ZXIudCgpKS5tYXBWYWx1ZSh4ID0+IHggKiA0MDApXG4gICkuc3Ryb2tlKClcbik7XG5cbiBcbmhlbHBlci5wbGF5RXhhbXBsZShcInN2Z1wiLCAxMCwgYW5pbWF0b3IsIDEwMCwgMTAwKTtcblxuZGVzY3JpYmUoJ3N2ZycsIGZ1bmN0aW9uICgpIHtcbiAgICBpdCAoJ3Nob3VsZCBtYXRjaCB0aGUgcmVmZXJlbmNlJywgZnVuY3Rpb24oZG9uZSkge1xuICAgICAgICBoZWxwZXIuc2FtZUV4YW1wbGUoXCJzdmdcIiwgXCJzdmctcmVmXCIsIGZ1bmN0aW9uKGVxdWFsKSB7XG4gICAgICAgICAgICBlcXVhbC5zaG91bGQuZXF1YWwodHJ1ZSk7XG4gICAgICAgICAgICBkb25lKCk7XG4gICAgICAgIH0pXG4gICAgfSk7XG59KTsiXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= 36 | -------------------------------------------------------------------------------- /dist/src/helper.js: -------------------------------------------------------------------------------- 1 | function __export(m) { 2 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 3 | } 4 | /// 5 | /// 6 | /// 7 | /// 8 | var Rx = require("rx"); 9 | var canvas = require("./canvas"); 10 | var Ax = require("./animaxe"); 11 | __export(require("./animaxe")); 12 | var ctx_get_transform_1 = require("./ctx-get-transform"); 13 | function getExampleAnimator(width, height) { 14 | if (width === void 0) { width = 100; } 15 | if (height === void 0) { height = 100; } 16 | try { 17 | // In a browser environment, find a canvas 18 | var canvas = document.getElementById("canvas"); 19 | console.log("browser", canvas); 20 | var context = canvas.getContext('2d'); 21 | ctx_get_transform_1.monkeyPatchCtxToAddGetTransform(context); //monkey patch context to get transform tracking 22 | var animator = new Ax.Animator(context); 23 | animator.registerEvents(canvas); 24 | return animator; 25 | } 26 | catch (err) { 27 | console.log("error, so assuming we are in node environment", err); 28 | // in a node.js environment, load a fake canvas 29 | console.log(err); 30 | var Canvas = require('canvas'); 31 | var canvas = new Canvas(width, height); 32 | console.log("node", canvas); 33 | var context = canvas.getContext('2d'); 34 | require('ctx-get-transform')(context); //monkey patch context to get transform tracking 35 | return new Ax.Animator(context); 36 | } 37 | } 38 | exports.getExampleAnimator = getExampleAnimator; 39 | function playExample(name, frames, animator, width, height) { 40 | try { 41 | //browser 42 | var time; 43 | var render = function () { 44 | window.requestAnimationFrame(render); 45 | var now = new Date().getTime(), dt = now - (time || now); 46 | time = now; 47 | animator.tick(dt * 0.001); 48 | }; 49 | render(); 50 | } 51 | catch (err) { 52 | console.log("error, so assuming we are in node environment", err); 53 | //node.js 54 | animator.play(canvas.save(width, height, "images/" + name + ".gif")); 55 | animator.ticker(Rx.Observable.return(0.1).repeat(Math.floor(frames))); 56 | } 57 | } 58 | exports.playExample = playExample; 59 | function sameExample(name, ref, cb) { 60 | try { 61 | throw new Error("not implemented"); 62 | } 63 | catch (err) { 64 | //node.js 65 | var cmp = require("file-compare"); 66 | var file1 = "images/" + name + ".gif"; 67 | var file2 = "images/" + ref + ".gif"; 68 | return cmp.compare(file1, file2, cb); 69 | } 70 | } 71 | exports.sameExample = sameExample; 72 | 73 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9oZWxwZXIudHMiXSwibmFtZXMiOlsiZ2V0RXhhbXBsZUFuaW1hdG9yIiwicGxheUV4YW1wbGUiLCJzYW1lRXhhbXBsZSJdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMERBQTBEO0FBQzFELDJDQUEyQztBQUMzQyw2Q0FBNkM7QUFDN0Msd0RBQXdEO0FBQ3hELElBQVksRUFBRSxXQUFNLElBQUksQ0FBQyxDQUFBO0FBQ3pCLElBQVksTUFBTSxXQUFNLFVBQVUsQ0FBQyxDQUFBO0FBQ25DLElBQVksRUFBRSxXQUFNLFdBQVcsQ0FBQyxDQUFBO0FBQ2hDLGlCQUFjLFdBQVcsQ0FBQyxFQUFBO0FBQzFCLGtDQUFnRSxxQkFBcUIsQ0FBQyxDQUFBO0FBRXRGLDRCQUFtQyxLQUFtQixFQUFFLE1BQW9CO0lBQXpDQSxxQkFBbUJBLEdBQW5CQSxXQUFtQkE7SUFBRUEsc0JBQW9CQSxHQUFwQkEsWUFBb0JBO0lBQ3hFQSxJQUFJQSxDQUFDQTtRQUNEQSwwQ0FBMENBO1FBQzFDQSxJQUFJQSxNQUFNQSxHQUFPQSxRQUFRQSxDQUFDQSxjQUFjQSxDQUFDQSxRQUFRQSxDQUFDQSxDQUFDQTtRQUNuREEsT0FBT0EsQ0FBQ0EsR0FBR0EsQ0FBQ0EsU0FBU0EsRUFBRUEsTUFBTUEsQ0FBQ0EsQ0FBQ0E7UUFDL0JBLElBQUlBLE9BQU9BLEdBQTZCQSxNQUFNQSxDQUFDQSxVQUFVQSxDQUFDQSxJQUFJQSxDQUFDQSxDQUFDQTtRQUVoRUEsbURBQWFBLENBQUNBLE9BQU9BLENBQUNBLENBQUNBLENBQUNBLGdEQUFnREE7UUFFeEVBLElBQUlBLFFBQVFBLEdBQUlBLElBQUlBLEVBQUVBLENBQUNBLFFBQVFBLENBQUNBLE9BQU9BLENBQUNBLENBQUNBO1FBRXpDQSxRQUFRQSxDQUFDQSxjQUFjQSxDQUFDQSxNQUFNQSxDQUFDQSxDQUFDQTtRQUNoQ0EsTUFBTUEsQ0FBQ0EsUUFBUUEsQ0FBQ0E7SUFDcEJBLENBQUVBO0lBQUFBLEtBQUtBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBLENBQUNBO1FBQ1hBLE9BQU9BLENBQUNBLEdBQUdBLENBQUNBLCtDQUErQ0EsRUFBRUEsR0FBR0EsQ0FBQ0EsQ0FBQ0E7UUFDbEVBLCtDQUErQ0E7UUFDL0NBLE9BQU9BLENBQUNBLEdBQUdBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBO1FBQ2pCQSxJQUFJQSxNQUFNQSxHQUFHQSxPQUFPQSxDQUFDQSxRQUFRQSxDQUFDQSxDQUFDQTtRQUMvQkEsSUFBSUEsTUFBTUEsR0FBR0EsSUFBSUEsTUFBTUEsQ0FBQ0EsS0FBS0EsRUFBRUEsTUFBTUEsQ0FBQ0EsQ0FBQ0E7UUFDdkNBLE9BQU9BLENBQUNBLEdBQUdBLENBQUNBLE1BQU1BLEVBQUVBLE1BQU1BLENBQUNBLENBQUNBO1FBRTVCQSxJQUFJQSxPQUFPQSxHQUE2QkEsTUFBTUEsQ0FBQ0EsVUFBVUEsQ0FBQ0EsSUFBSUEsQ0FBQ0EsQ0FBQ0E7UUFDaEVBLE9BQU9BLENBQUNBLG1CQUFtQkEsQ0FBQ0EsQ0FBQ0EsT0FBT0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsZ0RBQWdEQTtRQUN2RkEsTUFBTUEsQ0FBQ0EsSUFBSUEsRUFBRUEsQ0FBQ0EsUUFBUUEsQ0FBQ0EsT0FBT0EsQ0FBQ0EsQ0FBQ0E7SUFDcENBLENBQUNBO0FBQ0xBLENBQUNBO0FBekJlLDBCQUFrQixxQkF5QmpDLENBQUE7QUFFRCxxQkFBNEIsSUFBWSxFQUFFLE1BQWMsRUFBRSxRQUFxQixFQUFFLEtBQWUsRUFBRSxNQUFnQjtJQUM5R0MsSUFBSUEsQ0FBQ0E7UUFDREEsU0FBU0E7UUFDVEEsSUFBSUEsSUFBSUEsQ0FBQ0E7UUFDVEEsSUFBSUEsTUFBTUEsR0FBR0E7WUFDVCxNQUFNLENBQUMscUJBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDckMsSUFBSSxHQUFHLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxPQUFPLEVBQUUsRUFDMUIsRUFBRSxHQUFHLEdBQUcsR0FBRyxDQUFDLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztZQUM3QixJQUFJLEdBQUcsR0FBRyxDQUFDO1lBQ1gsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLEdBQUMsS0FBSyxDQUFDLENBQUM7UUFDNUIsQ0FBQyxDQUFDQTtRQUNGQSxNQUFNQSxFQUFFQSxDQUFDQTtJQUNiQSxDQUFFQTtJQUFBQSxLQUFLQSxDQUFBQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxDQUFDQTtRQUNWQSxPQUFPQSxDQUFDQSxHQUFHQSxDQUFDQSwrQ0FBK0NBLEVBQUVBLEdBQUdBLENBQUNBLENBQUNBO1FBQ2xFQSxTQUFTQTtRQUNUQSxRQUFRQSxDQUFDQSxJQUFJQSxDQUFDQSxNQUFNQSxDQUFDQSxJQUFJQSxDQUFDQSxLQUFLQSxFQUFFQSxNQUFNQSxFQUFFQSxTQUFTQSxHQUFHQSxJQUFJQSxHQUFHQSxNQUFNQSxDQUFDQSxDQUFDQSxDQUFDQTtRQUNyRUEsUUFBUUEsQ0FBQ0EsTUFBTUEsQ0FBQ0EsRUFBRUEsQ0FBQ0EsVUFBVUEsQ0FBQ0EsTUFBTUEsQ0FBQ0EsR0FBR0EsQ0FBQ0EsQ0FBQ0EsTUFBTUEsQ0FBQ0EsSUFBSUEsQ0FBQ0EsS0FBS0EsQ0FBQ0EsTUFBTUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7SUFDMUVBLENBQUNBO0FBQ0xBLENBQUNBO0FBbEJlLG1CQUFXLGNBa0IxQixDQUFBO0FBRUQscUJBQTRCLElBQVksRUFBRSxHQUFXLEVBQUUsRUFBcUI7SUFDeEVDLElBQUlBLENBQUNBO1FBQ0RBLE1BQU1BLElBQUlBLEtBQUtBLENBQUNBLGlCQUFpQkEsQ0FBQ0EsQ0FBQ0E7SUFDdkNBLENBQUVBO0lBQUFBLEtBQUtBLENBQUFBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBLENBQUNBO1FBQ1ZBLFNBQVNBO1FBQ1RBLElBQUlBLEdBQUdBLEdBQUdBLE9BQU9BLENBQUNBLGNBQWNBLENBQUNBLENBQUNBO1FBQ2xDQSxJQUFJQSxLQUFLQSxHQUFHQSxTQUFTQSxHQUFHQSxJQUFJQSxHQUFHQSxNQUFNQSxDQUFDQTtRQUN0Q0EsSUFBSUEsS0FBS0EsR0FBR0EsU0FBU0EsR0FBR0EsR0FBR0EsR0FBR0EsTUFBTUEsQ0FBQ0E7UUFDckNBLE1BQU1BLENBQUNBLEdBQUdBLENBQUNBLE9BQU9BLENBQUNBLEtBQUtBLEVBQUVBLEtBQUtBLEVBQUVBLEVBQUVBLENBQUNBLENBQUNBO0lBQ3pDQSxDQUFDQTtBQUNMQSxDQUFDQTtBQVZlLG1CQUFXLGNBVTFCLENBQUEiLCJmaWxlIjoic3JjL2hlbHBlci5qcyIsInNvdXJjZXNDb250ZW50IjpbbnVsbF0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 74 | -------------------------------------------------------------------------------- /peg/svg.peg: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2014 Gavin Kistner 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | { 24 | function merge(first,more){ 25 | for (var a=[first],i=0,l=more.length;i 3 | /// 4 | /// 5 | require('source-map-support').install(); 6 | require("should"); 7 | var Ax = require("../src/animaxe"); 8 | var helper = require("../src/helper"); 9 | var Parameter = require("../src/Parameter"); 10 | var animator = helper.getExampleAnimator(); 11 | function flowNode(pos, label, id, active) { 12 | return Ax.create() 13 | .translate(pos) 14 | .fillText(label, [0, 0]); 15 | } 16 | //each frame, first draw black background to erase the previous contents 17 | animator.play(Ax.create().fillStyle("#000000").fillRect([0, 0], [100, 100])); 18 | var timeline = Parameter.constant(1).take(1).then(Parameter.constant(1).take(2)); 19 | // move the drawing context frame of reference to the center (50,50) and then move it by a +ve x velocity, 20 | // so the frame of reference moves over time. 21 | // then draw our 2 frame spark animation in a loop so it draws forever 22 | animator.play(Ax.create().fillStyle("white") 23 | .parallel([ 24 | flowNode([10, 10], "Ax.create()", 0, timeline), 25 | flowNode([20, 20], "strokeStyle(\"green\")", 0, timeline), 26 | flowNode([30, 30], "parrallel([", 0, timeline), 27 | ])); 28 | // the helper function pipes injects the context, either from a web canvas or a fake node.js one. 29 | helper.playExample("readme_order", 20, animator, 100, 100); 30 | describe('readme_order', function () { 31 | it('should match the reference', function (done) { 32 | helper.sameExample("readme_order", "readme_order-ref", function (equal) { 33 | equal.should.equal(true); 34 | done(); 35 | }); 36 | }); 37 | }); 38 | 39 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImV4YW1wbGUudGVtcGxhdGUudHMiXSwibmFtZXMiOlsiZmxvd05vZGUiXSwibWFwcGluZ3MiOiJBQUFBLDJEQUEyRDtBQUMzRCw2Q0FBNkM7QUFDN0MsNENBQTRDO0FBQzVDLDJDQUEyQztBQUMzQyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztBQUN4QyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7QUFpQmxCLElBQVksRUFBRSxXQUFNLGdCQUFnQixDQUFDLENBQUE7QUFDckMsSUFBWSxNQUFNLFdBQU0sZUFBZSxDQUFDLENBQUE7QUFFeEMsSUFBWSxTQUFTLFdBQU0sa0JBQWtCLENBQUMsQ0FBQTtBQUU5QyxJQUFJLFFBQVEsR0FBZ0IsTUFBTSxDQUFDLGtCQUFrQixFQUFFLENBQUM7QUFFeEQsa0JBQ0ksR0FBYSxFQUNiLEtBQWEsRUFDYixFQUFVLEVBQ1YsTUFBbUM7SUFDbkNBLE1BQU1BLENBQUNBLEVBQUVBLENBQUNBLE1BQU1BLEVBQUVBO1NBQ2JBLFNBQVNBLENBQUNBLEdBQUdBLENBQUNBO1NBQ2RBLFFBQVFBLENBQUNBLEtBQUtBLEVBQUVBLENBQUNBLENBQUNBLEVBQUNBLENBQUNBLENBQUNBLENBQUNBLENBQUFBO0FBRS9CQSxDQUFDQTtBQUNELHdFQUF3RTtBQUN4RSxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsR0FBRyxFQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUUxRSxJQUFJLFFBQVEsR0FDUixTQUFTLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzlCLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUNoQyxDQUFBO0FBRUwsMEdBQTBHO0FBQzFHLDZDQUE2QztBQUM3QyxzRUFBc0U7QUFDdEUsUUFBUSxDQUFDLElBQUksQ0FDVCxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQztLQUM3QixRQUFRLENBQUM7SUFDTixRQUFRLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsYUFBYSxFQUFFLENBQUMsRUFBRSxRQUFRLENBQUM7SUFDOUMsUUFBUSxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLHdCQUF3QixFQUFFLENBQUMsRUFBRSxRQUFRLENBQUM7SUFDekQsUUFBUSxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLGFBQWEsRUFBRSxDQUFDLEVBQUUsUUFBUSxDQUFDO0NBQ2pELENBQUMsQ0FDTCxDQUFDO0FBRUYsaUdBQWlHO0FBQ2pHLE1BQU0sQ0FBQyxXQUFXLENBQUMsY0FBYyxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBRzNELFFBQVEsQ0FBQyxjQUFjLEVBQUU7SUFDckIsRUFBRSxDQUFFLDRCQUE0QixFQUFFLFVBQVMsSUFBSTtRQUMzQyxNQUFNLENBQUMsV0FBVyxDQUFDLGNBQWMsRUFBRSxrQkFBa0IsRUFBRSxVQUFTLEtBQUs7WUFDakUsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDekIsSUFBSSxFQUFFLENBQUM7UUFDWCxDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDLENBQUMiLCJmaWxlIjoiZGlzdC90ZXN0L3JlYWRtZV9vcmRlci5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFRISVMgSVMgQVVUTyBHRU5FUkFURUQgVEVTVCBDT0RFLCBETyBOT1QgTU9ESUZZIERJUkVDVExZXG4vLy8gPHJlZmVyZW5jZSBwYXRoPVwiLi4vdHlwZXMvc2hvdWxkLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL21vY2hhLmQudHNcIiAvPlxuLy8vIDxyZWZlcmVuY2UgcGF0aD1cIi4uL3R5cGVzL25vZGUuZC50c1wiIC8+XG5yZXF1aXJlKCdzb3VyY2UtbWFwLXN1cHBvcnQnKS5pbnN0YWxsKCk7XG5yZXF1aXJlKFwic2hvdWxkXCIpO1xuXG4vKipcbiAqIEFuIElsbGlzdHJhdGlvbiBvZiB0aGUgb3JkZXIgb2Ygb3BlcmF0aW9ucyBvZlxuICAgQXguY3JlYXRlKCkgIC8vIGJlZ2luIGFuIG5ldyBhbmltYXRpb24gdHJlZVxuICAuc3Ryb2tlU3R5bGUoXCJncmVlblwiKSAvLyB0b3Agb2YgYW5pbWF0aW9uIHRyZWUgdGhlIHN0eWxlIGlzIHNldCB0byBncmVlblxuICAucGFycmFsbGVsKFtcbiAgICBBeC5jcmVhdGUoKS5zdHJva2UoKSAvLyBzdHJva2UgZ3JlZW4sIGRvd25zdHJlYW0gb2YgcGFycmFsbGVsXG4gICAgQXguY3JlYXRlKCkuc3Ryb2tlU3R5bGUoXCJyZWRcIikuc3Ryb2tlKCksIC8vc3Ryb2tlIHJlZFxuICAgIEF4LmNyZWF0ZSgpLnN0cm9rZSgpIC8vIHN0cm9rZSBncmVlbiwgbm90IGFmZmVjdGVkIGJ5IHJlZCBzaWJsaW5nXG4gIF0pXG4gIC5zdHJva2UoKSAvLyBzdHJva2UgZ3JlZW4sIGRvd25zdHJlYW0gb2YgcGFycmFsbGVsIHdoaWNoIGlzIGRvd25zdHJlYW0gb2YgdG9wXG5dKVxuICovXG5cblxuaW1wb3J0ICogYXMgUnggZnJvbSBcInJ4XCI7XG5pbXBvcnQgKiBhcyBBeCBmcm9tIFwiLi4vc3JjL2FuaW1heGVcIjtcbmltcG9ydCAqIGFzIGhlbHBlciBmcm9tIFwiLi4vc3JjL2hlbHBlclwiO1xuaW1wb3J0ICogYXMgZXZlbnRzIGZyb20gXCIuLi9zcmMvZXZlbnRzXCI7XG5pbXBvcnQgKiBhcyBQYXJhbWV0ZXIgZnJvbSBcIi4uL3NyYy9QYXJhbWV0ZXJcIjtcblxudmFyIGFuaW1hdG9yOiBBeC5BbmltYXRvciA9IGhlbHBlci5nZXRFeGFtcGxlQW5pbWF0b3IoKTtcblxuZnVuY3Rpb24gZmxvd05vZGUoXG4gICAgcG9zOiBBeC5Qb2ludCwgXG4gICAgbGFiZWw6IHN0cmluZywgXG4gICAgaWQ6IG51bWJlciwgXG4gICAgYWN0aXZlOiBQYXJhbWV0ZXIuUGFyYW1ldGVyPG51bWJlcj4pOiBBeC5PcGVyYXRpb24geyAvL3dlIGNvdWxkIGJlIGNsZXZlciBhbmQgbGV0IHNwYXJrIHRha2UgYSBzZXEsIGJ1dCB1c2VyIGZ1bmN0aW9ucyBzaG91bGQgYmUgc2ltcGxlXG4gICAgcmV0dXJuIEF4LmNyZWF0ZSgpXG4gICAgICAgIC50cmFuc2xhdGUocG9zKVxuICAgICAgICAuZmlsbFRleHQobGFiZWwsIFswLDBdKVxuICAgICAgICBcbn1cbi8vZWFjaCBmcmFtZSwgZmlyc3QgZHJhdyBibGFjayBiYWNrZ3JvdW5kIHRvIGVyYXNlIHRoZSBwcmV2aW91cyBjb250ZW50c1xuYW5pbWF0b3IucGxheShBeC5jcmVhdGUoKS5maWxsU3R5bGUoXCIjMDAwMDAwXCIpLmZpbGxSZWN0KFswLDBdLFsxMDAsMTAwXSkpO1xuXG52YXIgdGltZWxpbmUgPSBcbiAgICBQYXJhbWV0ZXIuY29uc3RhbnQoMSkudGFrZSgxKS50aGVuKFxuICAgICAgICBQYXJhbWV0ZXIuY29uc3RhbnQoMSkudGFrZSgyKSAgICBcbiAgICApXG4gICAgXG4vLyBtb3ZlIHRoZSBkcmF3aW5nIGNvbnRleHQgZnJhbWUgb2YgcmVmZXJlbmNlIHRvIHRoZSBjZW50ZXIgKDUwLDUwKSBhbmQgdGhlbiBtb3ZlIGl0IGJ5IGEgK3ZlIHggdmVsb2NpdHksXG4vLyBzbyB0aGUgZnJhbWUgb2YgcmVmZXJlbmNlIG1vdmVzIG92ZXIgdGltZS5cbi8vIHRoZW4gZHJhdyBvdXIgMiBmcmFtZSBzcGFyayBhbmltYXRpb24gaW4gYSBsb29wIHNvIGl0IGRyYXdzIGZvcmV2ZXJcbmFuaW1hdG9yLnBsYXkoXG4gICAgQXguY3JlYXRlKCkuZmlsbFN0eWxlKFwid2hpdGVcIilcbiAgICAucGFyYWxsZWwoW1xuICAgICAgICBmbG93Tm9kZShbMTAsIDEwXSwgXCJBeC5jcmVhdGUoKVwiLCAwLCB0aW1lbGluZSksXG4gICAgICAgIGZsb3dOb2RlKFsyMCwgMjBdLCBcInN0cm9rZVN0eWxlKFxcXCJncmVlblxcXCIpXCIsIDAsIHRpbWVsaW5lKSxcbiAgICAgICAgZmxvd05vZGUoWzMwLCAzMF0sIFwicGFycmFsbGVsKFtcIiwgMCwgdGltZWxpbmUpLFxuICAgIF0pICAgXG4pO1xuXG4vLyB0aGUgaGVscGVyIGZ1bmN0aW9uIHBpcGVzIGluamVjdHMgdGhlIGNvbnRleHQsIGVpdGhlciBmcm9tIGEgd2ViIGNhbnZhcyBvciBhIGZha2Ugbm9kZS5qcyBvbmUuXG5oZWxwZXIucGxheUV4YW1wbGUoXCJyZWFkbWVfb3JkZXJcIiwgMjAsIGFuaW1hdG9yLCAxMDAsIDEwMCk7XG5cblxuZGVzY3JpYmUoJ3JlYWRtZV9vcmRlcicsIGZ1bmN0aW9uICgpIHtcbiAgICBpdCAoJ3Nob3VsZCBtYXRjaCB0aGUgcmVmZXJlbmNlJywgZnVuY3Rpb24oZG9uZSkge1xuICAgICAgICBoZWxwZXIuc2FtZUV4YW1wbGUoXCJyZWFkbWVfb3JkZXJcIiwgXCJyZWFkbWVfb3JkZXItcmVmXCIsIGZ1bmN0aW9uKGVxdWFsKSB7XG4gICAgICAgICAgICBlcXVhbC5zaG91bGQuZXF1YWwodHJ1ZSk7XG4gICAgICAgICAgICBkb25lKCk7XG4gICAgICAgIH0pXG4gICAgfSk7XG59KTsiXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= 40 | --------------------------------------------------------------------------------