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
74 var win
= gui
.Window
.get();
75 var nativeMenuBar
= new gui
.Menu({ type
: "menubar" });
76 if (nativeMenuBar
.createMacBuiltin
) {
77 nativeMenuBar
.createMacBuiltin(
78 document
.title
, { hideEdit
: true });
79 win
.menu
= nativeMenuBar
;
82 win
.on("minimize", function () {
83 var ev
= new Event("visibilitychange");
85 wkdoc
.dispatchEvent(ev
);
87 win
.on("restore", function () {
88 var ev
= new Event("visibilitychange");
90 wkdoc
.dispatchEvent(ev
);
94 return new Promise(function (resolve
) {
95 // TODO: Some kind of loading progress bar.
96 initOptions
= options
|| {};
97 yuu
.log("messages", "Initializing Yuu engine.");
99 yf
.each(function (hook
) {
100 promises
.push(hook
.call(yuu
, initOptions
));
102 initHooks
= null; // Bust future registerInitHook calls.
103 yuu
.log("messages", "Initialization hooks complete.");
105 gui
.Window
.get().show();
106 gui
.Window
.get().focus();
108 resolve(Promise
.all(yf
.filter(null, promises
)));
109 }).then(function () {
110 yuu
.log("messages", "Loading complete.");
111 }).catch(fatalError
);
114 yuu
.log
= yf
.argv(function (category
, args
) {
115 /** Log a message to the console.
117 This supports simple filtering by setting e.g.
118 `yuu.log.errors = true` to log anything with the
121 if (!category
|| this.log
[category
]) {
123 case "errors": return console
.error
.apply(console
, args
);
124 case "warnings": return console
.warn
.apply(console
, args
);
125 default: return console
.log
.apply(console
, args
);
130 yuu
.log
.errors
= true;
131 yuu
.log
.warnings
= true;
132 yuu
.log
.messages
= true;
134 yuu
.logError = function (e
) {
135 yuu
.log("errors", e
.message
|| "unknown error", e
);
138 yuu
.GET = function (url
, params
) {
139 /** Promise the HTTP GET the contents of a URL. */
140 return new Promise(function (resolve
, reject
) {
141 var req
= new XMLHttpRequest();
142 req
.open("GET", url
, true);
143 for (var k
in params
)
145 req
.onload = function () {
146 var status
= this.status
;
147 // status === 0 is given by node-webkit for success.
148 if ((status
>= 200 && status
< 300) || status
=== 0)
149 resolve(this.response
);
152 url
+ ": " + status
+ ": " + this.statusText
));
154 req
.onabort = function () { reject(new Error("aborted")); };
155 req
.onerror = function () { reject(new Error("network error")); };
156 req
.ontimeout = function () { reject(new Error("timed out")); };
161 yuu
.Image = function (src
) {
162 /** Promises a DOM Image. */
163 return new Promise(function (resolve
, reject
) {
164 var img
= new Image();
165 img
.onload = function () {
168 img
.onerror = function () {
169 var msg
= "Unable to load " + img
.src
;
170 yuu
.log("errors", msg
);
171 reject(new Error(msg
));
177 /** Command parsing and execution
179 The command API serves several roles. It is a way to enable or
180 disable different game logic within different scenes; capture
181 and replay or automate game events; loosely or late-bind game
182 modules; customize input mappings; and a debugging tool to
183 help inspect or modify the state of a running program.
185 A command is a string of a command name followed by arguments
186 separated by whitespace. It's similar to a fully bound
187 closure. It is less flexible but easier to inspect, store,
188 replay, and pass around.
190 Command names are mapped to functions, grouped into sets, and
191 the sets pushed onto a stack. They are executed by passing the
192 command string to the execute function which walks the stack
193 looking for the matching command.
195 If the command string is prefaced with + or -, true or false
196 are appended to the argument list. e.g. `+command` is
197 equivalent to `command true` and `-command 1 b` is equivalent
198 to `command 1 b false`. By convention, commands of those forms
199 return their internal state when called with neither true or
200 false. This is useful for another special prefix, ++. When
201 called as `++command`, it is executed with no arguments, the
202 result inverted (with !), and then called again passing that
203 inverted value as the last argument.
206 function isCommand (f
) {
207 return yf
.isFunction(f
) && f
._isCommandFunction
;
210 function cmdbind () {
211 // Commands are practically a subtype of functions. Binding
212 // them (which happens often, e.g. when Scenes register
213 // commands) should also return a command.
214 var f
= Function
.prototype.bind
.apply(this, arguments
);
215 // usage is still valid iff no new arguments were given.
216 return cmd(f
, arguments
.length
<= 1 && this.usage
, this.description
);
219 var cmd
= yuu
.cmd
= yf
.argcd(
220 /** Decorate a function for command execution
222 Command functions need some special attributes to work
223 correctly. This decorator makes sure they have them.
225 function (f
) { return yuu
.cmd(f
, null, null); },
226 function (f
, description
) { return yuu
.cmd(f
, null, description
); },
227 function (f
, usage
, description
) {
228 f
._isCommandFunction
= true;
229 f
.usage
= usage
|| " <value>".repeat(f
.length
).substring(1);
230 f
.description
= description
|| "no description provided";
236 yuu
.propcmd = function (o
, prop
, desc
, valspec
) {
237 /** Generate a command function that controls a property
239 A common pattern for command functions is to simply get or
240 set a single object property. This wrapper will generate a
241 correct function to do that.
243 valspec
= valspec
|| typeof o
[prop
];
244 desc
= desc
|| "Retrieve or modify the value of " + prop
;
245 return cmd(function () {
246 if (arguments
.length
)
247 o
[prop
] = arguments
[0];
249 }, "<" + valspec
+ "?>", desc
);
252 var QUOTED_SPLIT
= /[^"\s]+|"(?:\\"|[^"])+"/g;
253 var COMMAND_SPLIT
= /\s+(&&|\|\||;)\s+/g;
255 function parseParam (param
) {
256 if (yf
.head(param
) === "{" && yf
.last(param
) === "}")
257 return resolvePropertyPath(
258 this, param
.substr(1, param
.length
- 2));
259 try { return JSON
.parse(param
); }
260 catch (exc
) { return param
; }
263 function parseCommand (cmdstring
, ctx
) {
264 /** Parse a command string into an invocation object.
266 The command string has a form like `+quux 1 2 3` or
267 `foobar "hello world"`.
269 Multiple commands can be joined in one string with &&, ||,
270 or ;. To use these characters literally as a command
271 argument place them in quotes.
273 Arguments wrapped in {}s are interpreted as property paths
274 for the provided context object. `{x[0].y}` will resolve
275 `ctx.x[0].y` and put that into the arguments array. To
276 avoid this behavior and get a literal string bounded by
277 {}, JSON-encode the string beforehand (e.g. `"{x[0].y}"`).
279 The returned array contains objects with three properties:
280 `name` - the command name to execute
281 `args` - an array of objects to pass as arguments
282 `toggle` - if the command value should be toggled ('++')
284 `cond` - "&&", "||", or ";", indicating what kind of
285 conditional should be applied.
289 var conds
= cmdstring
.split(COMMAND_SPLIT
);
290 for (var i
= -1; i
< conds
.length
; i
+= 2) {
291 var args
= conds
[i
+ 1].match(QUOTED_SPLIT
).map(parseParam
, ctx
);
292 var name
= args
.shift();
294 if (name
[0] === "+" && name
[1] === "+") {
295 name
= name
.substring(2);
297 } else if (name
[0] === "+") {
298 name
= name
.substring(1);
300 } else if (name
[0] === "-") {
301 name
= name
.substring(1);
304 invs
.push({ name
: name
, args
: args
, toggle
: toggle
,
305 cond
: conds
[i
] || ";"});
310 yuu
.CommandStack
= yT({
311 constructor: function () {
312 /** A stack of command sets for command lookup and execution */
313 this._cmdsets
= yf
.slice(arguments
);
316 push: function (cmdset
) {
317 /** Add a command set to the lookup stack. */
318 this._cmdsets
= this._cmdsets
.concat(cmdset
);
321 remove: function (cmdset
) {
322 /** Remove a command set from the lookup stack. */
323 this._cmdsets
= yf
.without(this._cmdsets
, cmdset
);
326 insertBefore: function (cmdset
, before
) {
327 this._cmdsets
= yf
.insertBefore(
328 this._cmdsets
.slice(), cmdset
, before
);
331 execute: function (cmdstring
, ctx
) {
332 /* Execute a command given a command string.
334 The command stack is searched top-down for the first
335 command with a matching name, and it is invoked. No
336 other commands are called.
338 A command set may also provide a special function named
339 `$`. If no matching command name is found, this
340 function is called with the raw invocation object (the
341 result of yuu.parseCommand) and may return true to stop
342 processing as if the command had been found.
344 var invs
= parseCommand(cmdstring
, ctx
);
347 yf
.each
.call(this, function (inv
) {
348 if ((inv
.cond
=== "&&" && !cond
) || (inv
.cond
=== "||" && cond
))
350 if (!yf
.eachrUntil(function (cmdset
) {
351 var cmd
= cmdset
[inv
.name
];
354 inv
.args
.push(!cmd
.apply(null, inv
.args
));
355 yuu
.log("commands", "Executing:", inv
.name
,
356 inv
.args
.map(JSON
.stringify
).join(" "));
357 res
= cmd
.apply(null, inv
.args
);
358 cond
= res
=== undefined ? cond
: !!res
;
359 yuu
.log("commands", "Result:", JSON
.stringify(res
));
362 return cmdset
.$ && cmdset
.$(inv
);
364 yuu
.log("errors", "Unknown command", inv
.name
);
370 yuu
.extractCommands = function (object
) {
372 yf
.each(function (prop
) {
373 // Check the descriptor before checking the value, because
374 // checking the value of accessors (which should never be
375 // stable commands) is generally a bad idea during
376 // constructors, and command sets are often filled in during
378 if (yT
.isDataDescriptor(yT
.getPropertyDescriptor(object
, prop
))
379 && isCommand(object
[prop
]))
380 commands
[prop
] = object
[prop
].bind(object
);
381 }, yf
.allKeys(object
));
385 yuu
.commandStack
= new yuu
.CommandStack(yuu
.defaultCommands
= {
386 /** The default command stack and set. */
387 cmds
: yuu
.cmd(function (term
) {
390 yuu
.commandStack
._cmdsets
.forEach(function (cmdset
) {
391 for (var cmdname
in cmdset
) {
392 if (cmdname
.indexOf(term
) >= 0) {
393 var cmd
= cmdset
[cmdname
];
396 msg
= [cmdname
, cmd
.usage
, "--", cmd
.description
];
398 msg
= [cmdname
, "--", cmd
.description
];
399 cmds
.push(msg
.join(" "));
403 yuu
.log("messages", cmds
.join("\n"));
404 }, "<term?>", "display available commands (matching the term)"),
406 echo
: yuu
.cmd(function () {
407 yuu
.log("messages", arguments
);
408 }, "...", "echo arguments to the console"),
410 log
: yuu
.cmd(function (name
, state
) {
411 if (state
!== undefined)
412 yuu
.log
[name
] = !!state
;
413 return yuu
.log
[name
];
414 }, "<category> <boolean?>", "enable/disable a logging category")
418 yuu
.defaultCommands
.showDevTools
= yuu
.cmd(function () {
420 gui
.Window
.get().showDevTools();
421 }, "show developer tools");
423 var anchorPoint
= yuu
.anchorPoint
= yf
.argcd(
424 /** Calculate the anchor point for a box given extents and anchor mode
426 This function is the inverse of yuu.bottomLeft.
428 function (anchor
, aabb
) {
429 return anchorPoint(anchor
, aabb
.x0
, aabb
.y0
, aabb
.x1
, aabb
.y1
);
431 function (anchor
, x0
, y0
, x1
, y1
) {
433 case "center": return [(x0
+ x1
) / 2, (y0
+ y1
) / 2];
434 case "top": return [(x0
+ x1
) / 2, y1
];
435 case "bottom": return [(x0
+ x1
) / 2, y0
];
436 case "left": return [x0
, (y0
+ y1
) / 2];
437 case "right": return [x1
, (y0
+ y1
) / 2];
438 case "topleft": return [x0
, y1
];
439 case "topright": return [x1
, y1
];
440 case "bottomleft": return [x0
, y0
];
441 case "bottomright": return [x0
, y0
];
442 default: return [anchor
[0], anchor
[1]];
447 yuu
.bottomLeft = function (anchor
, x
, y
, w
, h
) {
448 /** Calculate the bottom-left for a box given size and anchor mode
450 This function is the inverse of yuu.anchorPoint.
453 case "center": return [x
- w
/ 2, y
- h
/ 2];
454 case "top": return [x
- w
/ 2, y
- h
];
455 case "bottom": return [x
- w
/ 2, y
];
456 case "left": return [x
, y
- h
/ 2];
457 case "right": return [x
- w
, y
- h
/ 2];
459 case "topleft": return [x
, y
- h
];
460 case "topright": return [x
- w
, y
- h
];
461 case "bottomleft": return [x
, y
];
462 case "bottomright": return [x
- w
, y
];
463 default: return [anchor
[0], anchor
[1]];
467 yuu
.lerp = function (a
, b
, p
) {
468 return (a
!== null && a
!== undefined && a
.lerp
)
469 ? a
.lerp(b
, p
) : (b
!== null && b
!== undefined && b
.lerp
)
470 ? b
.lerp(a
, 1 - p
) : p
< 0.5 ? a
: b
;
473 yuu
.bilerp = function (x0y0
, x1y0
, x0y1
, x1y1
, px
, py
) {
474 /** Bilinearly interpolate between four values in two dimensions */
475 return yuu
.lerp(yuu
.lerp(x0y0
, x1y0
, px
), yuu
.lerp(x0y1
, x1y1
, px
), py
);
478 function resolvePropertyPath (object
, path
) {
479 /** Look up a full property path
481 If a null is encountered in the path, this function returns
482 null. If undefined is encountered or a property is missing, it
485 var parts
= path
.replace(/\[(\w+)\]/g, '.$1').split('.');
487 i
< parts
.length
&& object
!== undefined && object
!== null;
489 object
= object
[parts
[i
]];
495 /** Somewhat like Python's random.Random.
497 Passed a function that returns a uniform random variable in
498 [0, 1) it can do other useful randomization algorithms.
500 Its methods are implemented straightforwardly rather than
501 rigorously - this means they may not behave correctly in
502 common edge cases like precision loss.
504 constructor: function (generator
) {
505 this.random
= generator
|| Math
.random
;
506 this._spareGauss
= null;
509 choice: function (seq
) {
510 /** Return a random element from the provided array. */
511 return seq
[this.randrange(0, seq
.length
)];
516 /** Return a uniform random integer in [0, a). */
517 return (this.random() * a
) | 0;
521 /** Return a uniform random integer in [a, b). */
524 return a
+ ((this.random() * (b
- a
)) | 0);
527 function (a
, b
, step
) {
528 /** Return a uniform random number in [a, b).
530 The number is constrained to values of a + i * step
531 where i is a non-negative integer.
533 var i
= Math
.ceil((b
- a
) / step
);
534 return a
+ this.randrange(i
) * step
;
540 /** Return a uniform random variable in [0, a). */
541 return a
* this.random();
544 /** Return a uniform random variable in [a, b). */
545 return a
+ (b
- a
) * this.random();
549 gauss: function (mean
, sigma
) {
550 var u
= this._spareGauss
, v
, s
;
551 this._spareGauss
= null;
555 u
= this.uniform(-1, 1);
556 v
= this.uniform(-1, 1);
558 } while (s
>= 1.0 || s
=== 0.0);
559 var t
= Math
.sqrt(-2.0 * Math
.log(s
) / s
);
560 this._spareGauss
= v
* t
;
563 return mean
+ sigma
* u
;
567 /** Return true the given percent of the time (default 50%). */
568 function () { return this.random() < 0.5; },
569 function (a
) { return this.random() < a
; }
572 randsign: function (v
) {
573 return this.randbool() ? v
: -v
;
576 shuffle: function (seq
) {
577 for (var i
= seq
.length
- 1; i
> 0; --i
) {
578 var index
= this.randrange(i
+ 1);
586 discard: function (z
) {
593 yuu
.createLCG
= yf
.argcd(
594 /** Linear congruential random generator
596 This returns a function that generates numbers [0, 1) as
597 with Math.random. You can also read or assign the `state`
598 attribute to set the internal state.
600 function () { return yuu
.createLCG(Math
.random() * 2147483647); },
602 var state
= seed
| 0;
603 return function generator () {
604 state
= (state
* 1664525 + 1013904223) % 4294967296;
605 return state
/ 4294967296;
610 yuu
.random
= new yuu
.Random();
612 function defaultKey (args
) {
613 // Cache things that can be constructed with one string.
614 return args
.length
=== 1 && yf
.isString(args
[0]) ? args
[0] : null;
617 yuu
.Caching = function (Type
, cacheKey
) {
619 var k
= ctor
._cacheKey(arguments
);
620 var o
= k
&& ctor
._cache
[k
];
622 o
= ctor
._cache
[k
] = yf
.construct(ctor
.Uncached
, arguments
);
625 ctor
._cacheKey
= cacheKey
|| defaultKey
;
627 ctor
.Uncached
= Type
;
631 yuu
.transpose2d = function (a
) {
632 for (var x
= 0; x
< a
.length
; ++x
) {
633 for (var y
= 0; y
< x
; ++y
) {
641 yuu
.normalizeRadians = function (theta
) {
643 return (theta
+ 3 * PI
) % (2 * PI
) - PI
;
646 yuu
.radians = function (v
) {
647 return v
* (Math
.PI
/ 180.0);
650 yuu
.degrees = function (v
) {
651 return v
* (180.0 / Math
.PI
);
654 var SHORT
= /(\/|^)@(.+)$/;
655 yuu
.resourcePath = function (path
, category
, ext
) {
657 if ((match
= path
.match(SHORT
))) {
658 path
= path
.replace(/^yuu\/@/, yuu
.PATH
+ "@")
659 .replace(SHORT
, "$1data/" + category
+ "/$2");
660 if (match
[2].indexOf(".") === -1)
666 yuu
.ready = function (resources
, result
) {
667 return Promise
.all(yf
.filter(null, yf
.pluck("ready", resources
)))
671 yuu
.openURL = function (url
) {
672 if (gui
&& gui
.Shell
)
673 gui
.Shell
.openExternal(url
);
678 function crossPlatformFilename (basename
) {
680 // Replace D/M/Y with D-M-Y, and H:M:S with H.M.S.
681 .replace(/\//g, "-").replace(/:/g
, ".")
682 // Replace all other problematic characters with _.
683 .replace(/["<>*?|\\]/g, "_");
686 yuu
.downloadURL = function (url
, suggestedName
) {
687 var regex
= /^data:[^;+]+;base64,(.*)$/;
688 var matches
= url
.match(regex
);
689 suggestedName
= crossPlatformFilename(suggestedName
);
691 var data
= matches
[1];
692 var buffer
= new Buffer(data
, 'base64');
693 var HOME
= process
.env
.HOME
694 || process
.env
.HOMEPATH
695 || process
.env
.USERPROFILE
;
696 var filename
= HOME
+ "/" + suggestedName
;
697 console
.log("Saving to", filename
);
698 fs
.writeFileSync(filename
, buffer
);
700 var link
= document
.createElement('a');
701 link
.style
.display
= "none";
703 link
.download
= suggestedName
;
704 // Firefox (as of 28) won't download from a link not rooted in
705 // the document; so, root it and then remove it when done.
706 document
.body
.appendChild(link
);
708 document
.body
.removeChild(link
);
713 constructor: yf
.argcd(
714 function () { this.constructor(0, 0, 0, 0); },
715 function (w
, h
) { this.constructor(0, 0, w
, h
); },
716 function (x0
, y0
, x1
, y1
) {
725 get: function () { return (this.x0
+ this.x1
) / 2; }
728 get: function () { return (this.y0
+ this.y1
) / 2; }
732 get: function () { return this.x1
- this.x0
; },
733 set: function (w
) { this.x1
= this.x0
+ w
; }
737 get: function () { return this.y1
- this.y0
; },
738 set: function (h
) { this.y1
= this.y0
+ h
; }
741 size
: { swizzle
: "wh" },
743 hw
: { get: function () { return this.w
/ 2; } },
744 hh
: { get: function () { return this.h
/ 2; } },
747 function (p
) { return this.contains(p
.x
, p
.y
); },
749 return x
>= this.x0
&& x
< this.x1
750 && y
>= this.y0
&& y
< this.y1
;
754 matchAspectRatio: function (outer
) {
755 var matched
= new this.constructor(
756 this.x0
, this.y0
, this.x1
, this.y1
);
757 var aRatio
= matched
.w
/ matched
.h
;
758 var bRatio
= outer
.w
/ outer
.h
;
759 if (aRatio
> bRatio
) {
760 // too wide, must be taller
761 var h
= matched
.w
/ bRatio
;
762 var dh
= h
- matched
.h
;
763 matched
.y0
-= dh
/ 2;
764 matched
.y1
+= dh
/ 2;
766 // too tall, must be wider
767 var w
= matched
.h
* bRatio
;
768 var dw
= w
- matched
.w
;
769 matched
.x0
-= dw
/ 2;
770 matched
.x1
+= dw
/ 2;
775 alignedInside: function (outer
, alignment
) {
783 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
787 x0
= outer
.x0
- this.w
;
792 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
795 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
796 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
799 x0
= outer
.x1
- this.w
;
800 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
804 y0
= outer
.y1
- this.h
;
807 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
808 y0
= outer
.y1
- this.h
;
811 x0
= outer
.x1
- this.w
;
812 y0
= outer
.y1
- this.h
;
815 return new this.constructor(x0
, y0
, x0
+ this.w
, y0
+ this.h
);
819 function splitPathExtension (path
) {
820 var dot
= path
.lastIndexOf(".");
821 if (dot
<= 0) return [path
, ""];
823 var dir
= path
.lastIndexOf("/");
824 if (dot
< dir
) return [path
, ""];
826 return [path
.substring(0, dot
), path
.substring(dot
)];
828 yuu
.splitPathExtension
= splitPathExtension
;
831 yT
.defineProperty(String
.prototype, "lerp", function (b
, p
) {
833 // Never numericLerp - if that's desired force Numbers.
834 // Be more conservative than stringLerp since this runs
835 // often and the diff can't be easily hoisted.
836 return this.length
* b
.length
> 256
837 ? stringLerp
.fastLerp(this, b
, p
)
838 : stringLerp
.diffLerp(this, b
, p
);
842 yT
.defineProperties(Number
.prototype, {
843 lerp: function (b
, p
) { return this + (b
- this) * p
; }
846 yT
.defineProperties(Array
.prototype, {
847 lerp: function (b
, p
) {
848 var length
= Math
.round(this.length
.lerp(b
.length
, p
));
849 var c
= new this.constructor(length
);
850 for (var i
= 0; i
< length
; ++i
) {
851 if (i
>= this.length
)
853 else if (i
>= b
.length
)
856 c
[i
] = this[i
].lerp(b
[i
], p
);
862 /** Typed array extensions
864 https://www.khronos.org/registry/typedarray/specs/1.0/
865 BUT: Read on for fun times in browser land~
867 Ideally we could just set these once on ArrayBufferView, but
868 the typed array specification doesn't require that such a
869 constructor actually exist. And in Firefox (18), it doesn't.
871 More infurating, in Safari (7.0.3) Int8Array etc. are not
872 functions so this needs to be added to the prototype
873 directly. This is a violation of the specification which
874 requires such constructors, and ECMA which requires
875 constructors be functions, and common decency.
878 [ Float32Array
, Float64Array
, Int8Array
, Uint8Array
,
879 Int16Array
, Uint16Array
, Int32Array
, Uint32Array
880 ].forEach(function (A
) {
881 yT
.defineProperties(A
.prototype, {
883 /** Like Array's slice, but for typed arrays */
884 function () { return new this.constructor(this); },
886 return new this.constructor(this.subarray(begin
));
888 function (begin
, end
) {
889 return new this.constructor(this.subarray(begin
, end
));
893 fill
: Array
.prototype.fill
,
894 reverse
: Array
.prototype.reverse
,
896 lerp: function (b
, p
) {
901 var c
= new this.constructor(this.length
);
902 for (var i
= 0; i
< this.length
; ++i
)
903 c
[i
] = this[i
] + (b
[i
] - this[i
]) * p
;
909 }).call(typeof exports
=== "undefined" ? this : exports
,
910 typeof exports
=== "undefined" ? (this.yuu
= {}) : exports
);