1 /* Copyright 2014 Yukkuri Games
2 Licensed under the terms of the GNU GPL v2 or later
3 @license http://www.gnu.org/licenses/gpl-2.0.html
4 @source: http://yukkurigames.com/yuu/
11 yuu
.require = function (m
) {
12 try { return require(m
); }
13 catch (exc
) { return null; }
19 var yT
= this.yT
|| require("./yT");
20 var yf
= this.yf
|| require("./yf");
21 var gui
= yuu
.require("nw.gui");
22 var fs
= yuu
.require("fs");
23 var stringLerp
= this.stringLerp
|| yuu
.require("string-lerp");
26 var initOptions
= null;
28 if (typeof document
!== "undefined") {
29 var scripts
= document
.getElementsByTagName('script');
30 var path
= yf
.last(scripts
).src
.split('?')[0];
31 yuu
.PATH
= path
.split('/').slice(0, -1).join('/') + '/';
33 yuu
.PATH
= "file://" + escape(module
.__dirname
) + "/";
36 yuu
.registerInitHook
= initHooks
.push
.bind(initHooks
);
37 /** Register a hook to be called during Yuu initialization
39 Hooks are called in registration order with the module and
40 the options dictionary passed to the init method. (This is
41 also set to the module.)
44 function showError (exc
, kind
) {
45 var prefix
= "yuu-" + (kind
|| "") + "error";
47 var dialog
= document
.getElementById(prefix
);
48 var errorMessage
= document
.getElementById(prefix
+ "-message");
50 errorMessage
.textContent
= exc
.message
;
51 var errorStack
= document
.getElementById(prefix
+ "-stack");
53 errorStack
.textContent
= exc
.message
+ "\n\n" + exc
.stack
;
56 yuu
.showError
= showError
;
58 function fatalError (exc
) {
59 var dialog
= showError(exc
, "fatal-");
61 dialog
.style
.display
= "block";
63 gui
.Window
.get().show();
64 gui
.Window
.get().focus();
69 yuu
.init = function (options
) {
70 /** Initialize Yuu and call all registered hooks
73 document
.body
.className
+= (navigator
.standalone
|| gui
)
74 ? " standalone" : " browser";
77 var win
= gui
.Window
.get();
78 var nativeMenuBar
= new gui
.Menu({ type
: "menubar" });
79 if (nativeMenuBar
.createMacBuiltin
) {
80 nativeMenuBar
.createMacBuiltin(
81 document
.title
, { hideEdit
: true });
82 win
.menu
= nativeMenuBar
;
85 win
.on("minimize", function () {
86 var ev
= new Event("visibilitychange");
88 wkdoc
.dispatchEvent(ev
);
90 win
.on("restore", function () {
91 var ev
= new Event("visibilitychange");
93 wkdoc
.dispatchEvent(ev
);
95 win
.on('new-win-policy', function (frame
, url
, policy
) {
96 if (url
.startsWith('chrome'))
97 policy
.forceNewPopup();
99 policy
.forceDownload();
103 return new Promise(function (resolve
) {
104 // TODO: Some kind of loading progress bar.
105 initOptions
= options
|| {};
106 yuu
.log("messages", "Initializing Yuu engine.");
108 // initHooks can be pushed to while iterating, so iterate
109 // by index, not a foreach loop.
110 for (var i
= 0; i
< initHooks
.length
; ++i
)
111 promises
.push(initHooks
[i
].call(yuu
, initOptions
));
112 initHooks
= null; // Bust future registerInitHook calls.
113 yuu
.log("messages", "Initialization hooks complete.");
115 gui
.Window
.get().show();
116 gui
.Window
.get().focus();
118 resolve(Promise
.all(yf
.filter(null, promises
)));
119 }).then(function () {
120 yuu
.log("messages", "Loading complete.");
121 }).catch(fatalError
);
124 yuu
.log
= yf
.argv(function (category
, args
) {
125 /** Log a message to the console.
127 This supports simple filtering by setting e.g.
128 `yuu.log.errors = true` to log anything with the
131 if (!category
|| this.log
[category
]) {
133 case "errors": return console
.error
.apply(console
, args
);
134 case "warnings": return console
.warn
.apply(console
, args
);
135 default: return console
.log
.apply(console
, args
);
140 yuu
.log
.errors
= true;
141 yuu
.log
.warnings
= true;
142 yuu
.log
.messages
= true;
144 yuu
.logError = function (e
) {
145 yuu
.log("errors", e
.message
|| "unknown error", e
);
148 yuu
.GET = function (url
, params
) {
149 /** Promise the HTTP GET the contents of a URL. */
150 return new Promise(function (resolve
, reject
) {
151 var req
= new XMLHttpRequest();
152 req
.open("GET", url
, true);
153 for (var k
in params
)
155 req
.onload = function () {
156 var status
= this.status
;
157 // status === 0 is given by node-webkit for success.
158 if ((status
>= 200 && status
< 300) || status
=== 0)
159 resolve(this.response
);
162 url
+ ": " + status
+ ": " + this.statusText
));
164 req
.onabort = function () { reject(new Error("aborted")); };
165 req
.onerror = function () { reject(new Error("network error")); };
166 req
.ontimeout = function () { reject(new Error("timed out")); };
171 yuu
.Image = function (src
) {
172 /** Promises a DOM Image. */
173 return new Promise(function (resolve
, reject
) {
174 var img
= new Image();
175 img
.onload = function () {
178 img
.onerror = function () {
179 var msg
= "Unable to load " + img
.src
;
180 yuu
.log("errors", msg
);
181 reject(new Error(msg
));
187 /** Command parsing and execution
189 The command API serves several roles. It is a way to enable or
190 disable different game logic within different scenes; capture
191 and replay or automate game events; loosely or late-bind game
192 modules; customize input mappings; and a debugging tool to
193 help inspect or modify the state of a running program.
195 A command is a string of a command name followed by arguments
196 separated by whitespace. It's similar to a fully bound
197 closure. It is less flexible but easier to inspect, store,
198 replay, and pass around.
200 Command names are mapped to functions, grouped into sets, and
201 the sets pushed onto a stack. They are executed by passing the
202 command string to the execute function which walks the stack
203 looking for the matching command.
205 If the command string is prefaced with + or -, true or false
206 are appended to the argument list. e.g. `+command` is
207 equivalent to `command true` and `-command 1 b` is equivalent
208 to `command 1 b false`. By convention, commands of those forms
209 return their internal state when called with neither true or
210 false. This is useful for another special prefix, ++. When
211 called as `++command`, it is executed with no arguments, the
212 result inverted (with !), and then called again passing that
213 inverted value as the last argument.
216 function isCommand (f
) {
217 return yf
.isFunction(f
) && f
._isCommandFunction
;
220 function cmdbind () {
221 // Commands are practically a subtype of functions. Binding
222 // them (which happens often, e.g. when Scenes register
223 // commands) should also return a command.
224 var f
= Function
.prototype.bind
.apply(this, arguments
);
225 // usage is still valid iff no new arguments were given.
226 return cmd(f
, arguments
.length
<= 1 && this.usage
, this.description
);
229 var cmd
= yuu
.cmd
= yf
.argcd(
230 /** Decorate a function for command execution
232 Command functions need some special attributes to work
233 correctly. This decorator makes sure they have them.
235 function (f
) { return yuu
.cmd(f
, null, null); },
236 function (f
, description
) { return yuu
.cmd(f
, null, description
); },
237 function (f
, usage
, description
) {
238 f
._isCommandFunction
= true;
239 f
.usage
= usage
|| " <value>".repeat(f
.length
).substring(1);
240 f
.description
= description
|| "no description provided";
246 yuu
.propcmd = function (o
, prop
, desc
, valspec
) {
247 /** Generate a command function that controls a property
249 A common pattern for command functions is to simply get or
250 set a single object property. This wrapper will generate a
251 correct function to do that.
253 valspec
= valspec
|| typeof o
[prop
];
254 desc
= desc
|| "Retrieve or modify the value of " + prop
;
255 return cmd(function () {
256 if (arguments
.length
)
257 o
[prop
] = arguments
[0];
259 }, "<" + valspec
+ "?>", desc
);
262 var QUOTED_SPLIT
= /[^"\s]+|"(?:\\"|[^"])+"/g;
263 var COMMAND_SPLIT
= /\s+(&&|\|\||;)\s+/g;
265 function parseParam (param
) {
266 if (yf
.head(param
) === "{" && yf
.last(param
) === "}")
267 return resolvePropertyPath(
268 this, param
.substr(1, param
.length
- 2));
269 try { return JSON
.parse(param
); }
270 catch (exc
) { return param
; }
273 function parseCommand (cmdstring
, ctx
) {
274 /** Parse a command string into an invocation object.
276 The command string has a form like `+quux 1 2 3` or
277 `foobar "hello world"`.
279 Multiple commands can be joined in one string with &&, ||,
280 or ;. To use these characters literally as a command
281 argument place them in quotes.
283 Arguments wrapped in {}s are interpreted as property paths
284 for the provided context object. `{x[0].y}` will resolve
285 `ctx.x[0].y` and put that into the arguments array. To
286 avoid this behavior and get a literal string bounded by
287 {}, JSON-encode the string beforehand (e.g. `"{x[0].y}"`).
289 The returned array contains objects with three properties:
290 `name` - the command name to execute
291 `args` - an array of objects to pass as arguments
292 `toggle` - if the command value should be toggled ('++')
294 `cond` - "&&", "||", or ";", indicating what kind of
295 conditional should be applied.
299 var conds
= cmdstring
.split(COMMAND_SPLIT
);
300 for (var i
= -1; i
< conds
.length
; i
+= 2) {
301 var args
= conds
[i
+ 1].match(QUOTED_SPLIT
).map(parseParam
, ctx
);
302 var name
= args
.shift();
304 if (name
[0] === "+" && name
[1] === "+") {
305 name
= name
.substring(2);
307 } else if (name
[0] === "+") {
308 name
= name
.substring(1);
310 } else if (name
[0] === "-") {
311 name
= name
.substring(1);
314 invs
.push({ name
: name
, args
: args
, toggle
: toggle
,
315 cond
: conds
[i
] || ";"});
320 yuu
.CommandStack
= yT({
321 constructor: function () {
322 /** A stack of command sets for command lookup and execution */
323 this._cmdsets
= yf
.slice(arguments
);
326 push: function (cmdset
) {
327 /** Add a command set to the lookup stack. */
328 this._cmdsets
= this._cmdsets
.concat(cmdset
);
331 remove: function (cmdset
) {
332 /** Remove a command set from the lookup stack. */
333 this._cmdsets
= yf
.without(this._cmdsets
, cmdset
);
336 insertBefore: function (cmdset
, before
) {
337 this._cmdsets
= yf
.insertBefore(
338 this._cmdsets
.slice(), cmdset
, before
);
341 execute: function (cmdstring
, ctx
) {
342 /* Execute a command given a command string.
344 The command stack is searched top-down for the first
345 command with a matching name, and it is invoked. No
346 other commands are called.
348 A command set may also provide a special function named
349 `$`. If no matching command name is found, this
350 function is called with the raw invocation object (the
351 result of yuu.parseCommand) and may return true to stop
352 processing as if the command had been found.
354 var invs
= parseCommand(cmdstring
, ctx
);
357 yf
.each
.call(this, function (inv
) {
358 if ((inv
.cond
=== "&&" && !cond
) || (inv
.cond
=== "||" && cond
))
360 if (!yf
.eachrUntil(function (cmdset
) {
361 var cmd
= cmdset
[inv
.name
];
364 inv
.args
.push(!cmd
.apply(null, inv
.args
));
365 yuu
.log("commands", "Executing:", inv
.name
,
366 inv
.args
.map(JSON
.stringify
).join(" "));
367 res
= cmd
.apply(null, inv
.args
);
368 cond
= res
=== undefined ? cond
: !!res
;
369 yuu
.log("commands", "Result:", JSON
.stringify(res
));
372 return cmdset
.$ && cmdset
.$(inv
);
374 yuu
.log("errors", "Unknown command", inv
.name
);
380 yuu
.extractCommands = function (object
) {
382 yf
.each(function (prop
) {
383 // Check the descriptor before checking the value, because
384 // checking the value of accessors (which should never be
385 // stable commands) is generally a bad idea during
386 // constructors, and command sets are often filled in during
388 if (yT
.isDataDescriptor(yT
.getPropertyDescriptor(object
, prop
))
389 && isCommand(object
[prop
]))
390 commands
[prop
] = object
[prop
].bind(object
);
391 }, yf
.allKeys(object
));
395 yuu
.commandStack
= new yuu
.CommandStack(yuu
.defaultCommands
= {
396 /** The default command stack and set. */
397 cmds
: yuu
.cmd(function (term
) {
400 yuu
.commandStack
._cmdsets
.forEach(function (cmdset
) {
401 for (var cmdname
in cmdset
) {
402 if (cmdname
.indexOf(term
) >= 0) {
403 var cmd
= cmdset
[cmdname
];
406 msg
= [cmdname
, cmd
.usage
, "--", cmd
.description
];
408 msg
= [cmdname
, "--", cmd
.description
];
409 cmds
.push(msg
.join(" "));
413 yuu
.log("messages", cmds
.join("\n"));
414 }, "<term?>", "display available commands (matching the term)"),
416 echo
: yuu
.cmd(function () {
417 yuu
.log("messages", arguments
);
418 }, "...", "echo arguments to the console"),
420 log
: yuu
.cmd(function (name
, state
) {
421 if (state
!== undefined)
422 yuu
.log
[name
] = !!state
;
423 return yuu
.log
[name
];
424 }, "<category> <boolean?>", "enable/disable a logging category")
428 yuu
.defaultCommands
.showDevTools
= yuu
.cmd(function () {
430 gui
.Window
.get().showDevTools();
431 }, "show developer tools");
433 yuu
.anchorPoint = function (anchor
, x0
, y0
, x1
, y1
) {
434 /** Calculate the anchor point for a box given extents and anchor mode
436 This function is the inverse of yuu.bottomLeft.
439 case "center": return [(x0
+ x1
) / 2, (y0
+ y1
) / 2];
440 case "top": return [(x0
+ x1
) / 2, y1
];
441 case "bottom": return [(x0
+ x1
) / 2, y0
];
442 case "left": return [x0
, (y0
+ y1
) / 2];
443 case "right": return [x1
, (y0
+ y1
) / 2];
445 case "topleft": return [x0
, y1
];
446 case "topright": return [x1
, y1
];
447 case "bottomleft": return [x0
, y0
];
448 case "bottomright": return [x0
, y0
];
449 default: return [anchor
[0], anchor
[1]];
453 yuu
.bottomLeft = function (anchor
, x
, y
, w
, h
) {
454 /** Calculate the bottom-left for a box given size and anchor mode
456 This function is the inverse of yuu.anchorPoint.
459 case "center": return [x
- w
/ 2, y
- h
/ 2];
460 case "top": return [x
- w
/ 2, y
- h
];
461 case "bottom": return [x
- w
/ 2, y
];
462 case "left": return [x
, y
- h
/ 2];
463 case "right": return [x
- w
, y
- h
/ 2];
465 case "topleft": return [x
, y
- h
];
466 case "topright": return [x
- w
, y
- h
];
467 case "bottomleft": return [x
, y
];
468 case "bottomright": return [x
- w
, y
];
469 default: return [anchor
[0], anchor
[1]];
473 yuu
.lerp = function (a
, b
, p
) {
474 return (a
!== null && a
!== undefined && a
.lerp
)
475 ? a
.lerp(b
, p
) : (b
!== null && b
!== undefined && b
.lerp
)
476 ? b
.lerp(a
, 1 - p
) : p
< 0.5 ? a
: b
;
479 yuu
.bilerp = function (x0y0
, x1y0
, x0y1
, x1y1
, px
, py
) {
480 /** Bilinearly interpolate between four values in two dimensions */
481 return yuu
.lerp(yuu
.lerp(x0y0
, x1y0
, px
), yuu
.lerp(x0y1
, x1y1
, px
), py
);
484 function resolvePropertyPath (object
, path
) {
485 /** Look up a full property path
487 If a null is encountered in the path, this function returns
488 null. If undefined is encountered or a property is missing, it
491 var parts
= path
.replace(/\[(\w+)\]/g, '.$1').split('.');
493 i
< parts
.length
&& object
!== undefined && object
!== null;
495 object
= object
[parts
[i
]];
501 /** Somewhat like Python's random.Random.
503 Passed a function that returns a uniform random variable in
504 [0, 1) it can do other useful randomization algorithms.
506 Its methods are implemented straightforwardly rather than
507 rigorously - this means they may not behave correctly in
508 common edge cases like precision loss.
510 constructor: function (generator
) {
511 this.random
= generator
|| Math
.random
;
512 this._spareGauss
= null;
515 choice: function (seq
) {
516 /** Return a random element from the provided array. */
517 return seq
[this.randrange(0, seq
.length
)];
522 /** Return a uniform random integer in [0, a). */
523 return (this.random() * a
) | 0;
527 /** Return a uniform random integer in [a, b). */
530 return a
+ ((this.random() * (b
- a
)) | 0);
533 function (a
, b
, step
) {
534 /** Return a uniform random number in [a, b).
536 The number is constrained to values of a + i * step
537 where i is a non-negative integer.
539 var i
= Math
.ceil((b
- a
) / step
);
540 return a
+ this.randrange(i
) * step
;
546 /** Return a uniform random variable in [0, a). */
547 return a
* this.random();
550 /** Return a uniform random variable in [a, b). */
551 return a
+ (b
- a
) * this.random();
555 gauss: function (mean
, sigma
) {
556 var u
= this._spareGauss
, v
, s
;
557 this._spareGauss
= null;
561 u
= this.uniform(-1, 1);
562 v
= this.uniform(-1, 1);
564 } while (s
>= 1.0 || s
=== 0.0);
565 var t
= Math
.sqrt(-2.0 * Math
.log(s
) / s
);
566 this._spareGauss
= v
* t
;
569 return mean
+ sigma
* u
;
573 /** Return true the given percent of the time (default 50%). */
574 function () { return this.random() < 0.5; },
575 function (a
) { return this.random() < a
; }
578 randsign: function (v
) {
579 return this.randbool() ? v
: -v
;
582 shuffle: function (seq
) {
583 for (var i
= seq
.length
- 1; i
> 0; --i
) {
584 var index
= this.randrange(i
+ 1);
592 discard: function (z
) {
599 yuu
.createLCG
= yf
.argcd(
600 /** Linear congruential random generator
602 This returns a function that generates numbers [0, 1) as
603 with Math.random. You can also read or assign the `state`
604 attribute to set the internal state.
606 function () { return yuu
.createLCG(Math
.random() * 2147483647); },
608 var state
= seed
| 0;
609 return function generator () {
610 state
= (state
* 1664525 + 1013904223) % 4294967296;
611 return state
/ 4294967296;
616 yuu
.random
= new yuu
.Random();
618 function defaultKey (args
) {
619 // Cache things that can be constructed with one string.
620 return args
.length
=== 1 && yf
.isString(args
[0]) ? args
[0] : null;
623 yuu
.Caching = function (Type
, cacheKey
) {
625 var k
= ctor
._cacheKey(arguments
);
626 var o
= k
&& ctor
._cache
[k
];
628 o
= ctor
._cache
[k
] = yf
.construct(ctor
.Uncached
, arguments
);
631 ctor
._cacheKey
= cacheKey
|| defaultKey
;
633 ctor
.Uncached
= Type
;
637 yuu
.transpose2d = function (a
) {
638 for (var x
= 0; x
< a
.length
; ++x
) {
639 for (var y
= 0; y
< x
; ++y
) {
647 yuu
.normalizeRadians = function (theta
) {
649 return (theta
+ 3 * PI
) % (2 * PI
) - PI
;
652 yuu
.radians = function (v
) {
653 return v
* (Math
.PI
/ 180.0);
656 yuu
.degrees = function (v
) {
657 return v
* (180.0 / Math
.PI
);
660 var SHORT
= /(\/|^)@(.+)$/;
661 yuu
.resourcePath = function (path
, category
, ext
) {
663 if ((match
= path
.match(SHORT
))) {
664 path
= path
.replace(/^yuu\/@/, yuu
.PATH
+ "@")
665 .replace(SHORT
, "$1data/" + category
+ "/$2");
666 if (match
[2].indexOf(".") === -1)
672 yuu
.ready = function (resources
, result
) {
673 return Promise
.all(yf
.filter(null, yf
.pluck("ready", resources
)))
677 yuu
.openURL = function (url
) {
678 if (gui
&& gui
.Shell
)
679 gui
.Shell
.openExternal(url
);
684 function crossPlatformFilename (basename
) {
686 // Replace D/M/Y with D-M-Y, and H:M:S with H.M.S.
687 .replace(/\//g, "-").replace(/:/g
, ".")
688 // Replace all other problematic characters with _.
689 .replace(/["<>*?|\\]/g, "_");
692 yuu
.downloadURL = function (url
, suggestedName
) {
693 var regex
= /^data:[^;+]+;base64,(.*)$/;
694 var matches
= url
.match(regex
);
695 suggestedName
= crossPlatformFilename(suggestedName
);
697 var data
= matches
[1];
698 var buffer
= new Buffer(data
, 'base64');
699 var HOME
= process
.env
.HOME
700 || process
.env
.HOMEPATH
701 || process
.env
.USERPROFILE
;
702 var filename
= HOME
+ "/" + suggestedName
;
703 console
.log("Saving to", filename
);
704 fs
.writeFileSync(filename
, buffer
);
706 var link
= document
.createElement('a');
707 link
.style
.display
= "none";
709 link
.download
= suggestedName
;
710 // Firefox (as of 28) won't download from a link not rooted in
711 // the document; so, root it and then remove it when done.
712 document
.body
.appendChild(link
);
714 document
.body
.removeChild(link
);
719 constructor: yf
.argcd(
720 function () { this.constructor(0, 0, 0, 0); },
721 function (w
, h
) { this.constructor(0, 0, w
, h
); },
722 function (x0
, y0
, x1
, y1
) {
731 get: function () { return this.x1
- this.x0
; },
732 set: function (w
) { this.x1
= this.x0
+ w
; }
736 get: function () { return this.y1
- this.y0
; },
737 set: function (h
) { this.y1
= this.y0
+ h
; }
740 size
: { swizzle
: "wh" },
743 function (p
) { return this.contains(p
.x
, p
.y
); },
745 return x
>= this.x0
&& x
< this.x1
746 && y
>= this.y0
&& y
< this.y1
;
750 matchAspectRatio: function (outer
) {
751 var matched
= new this.constructor(
752 this.x0
, this.y0
, this.x1
, this.y1
);
753 var aRatio
= matched
.w
/ matched
.h
;
754 var bRatio
= outer
.w
/ outer
.h
;
755 if (aRatio
> bRatio
) {
756 // too wide, must be taller
757 var h
= matched
.w
/ bRatio
;
758 var dh
= h
- matched
.h
;
759 matched
.y0
-= dh
/ 2;
760 matched
.y1
+= dh
/ 2;
762 // too tall, must be wider
763 var w
= matched
.h
* bRatio
;
764 var dw
= w
- matched
.w
;
765 matched
.x0
-= dw
/ 2;
766 matched
.x1
+= dw
/ 2;
771 alignedInside: function (outer
, alignment
) {
779 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
783 x0
= outer
.x0
- this.w
;
788 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
791 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
792 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
795 x0
= outer
.x1
- this.w
;
796 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
800 y0
= outer
.y1
- this.h
;
803 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
804 y0
= outer
.y1
- this.h
;
807 x0
= outer
.x1
- this.w
;
808 y0
= outer
.y1
- this.h
;
811 return new this.constructor(x0
, y0
, x0
+ this.w
, y0
+ this.h
);
815 function splitPathExtension (path
) {
816 var dot
= path
.lastIndexOf(".");
817 if (dot
<= 0) return [path
, ""];
819 var dir
= path
.lastIndexOf("/");
820 if (dot
< dir
) return [path
, ""];
822 return [path
.substring(0, dot
), path
.substring(dot
)];
824 yuu
.splitPathExtension
= splitPathExtension
;
827 yT
.defineProperty(String
.prototype, "lerp", function (b
, p
) {
829 // Never numericLerp - if that's desired force Numbers.
830 // Be more conservative than stringLerp since this runs
831 // often and the diff can't be easily hoisted.
832 return this.length
* b
.length
> 256
833 ? stringLerp
.fastLerp(this, b
, p
)
834 : stringLerp
.diffLerp(this, b
, p
);
838 yT
.defineProperties(Number
.prototype, {
839 lerp: function (b
, p
) { return this + (b
- this) * p
; }
842 yT
.defineProperties(Array
.prototype, {
843 lerp: function (b
, p
) {
844 var length
= Math
.round(this.length
.lerp(b
.length
, p
));
845 var c
= new this.constructor(length
);
846 for (var i
= 0; i
< length
; ++i
) {
847 if (i
>= this.length
)
849 else if (i
>= b
.length
)
852 c
[i
] = this[i
].lerp(b
[i
], p
);
858 /** Typed array extensions
860 https://www.khronos.org/registry/typedarray/specs/1.0/
861 BUT: Read on for fun times in browser land~
863 Ideally we could just set these once on ArrayBufferView, but
864 the typed array specification doesn't require that such a
865 constructor actually exist. And in Firefox (18), it doesn't.
867 More infurating, in Safari (7.0.3) Int8Array etc. are not
868 functions so this needs to be added to the prototype
869 directly. This is a violation of the specification which
870 requires such constructors, and ECMA which requires
871 constructors be functions, and common decency.
874 [ Float32Array
, Float64Array
, Int8Array
, Uint8Array
,
875 Int16Array
, Uint16Array
, Int32Array
, Uint32Array
876 ].forEach(function (A
) {
877 yT
.defineProperties(A
.prototype, {
879 /** Like Array's slice, but for typed arrays */
880 function () { return new this.constructor(this); },
882 return new this.constructor(this.subarray(begin
));
884 function (begin
, end
) {
885 return new this.constructor(this.subarray(begin
, end
));
889 fill
: Array
.prototype.fill
,
890 reverse
: Array
.prototype.reverse
,
892 lerp: function (b
, p
) {
897 var c
= new this.constructor(this.length
);
898 for (var i
= 0; i
< this.length
; ++i
)
899 c
[i
] = this[i
] + (b
[i
] - this[i
]) * p
;
905 }).call(typeof exports
=== "undefined" ? this : exports
,
906 typeof exports
=== "undefined" ? (this.yuu
= {}) : exports
);