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 yuu
.anchorPoint = function (anchor
, x0
, y0
, x1
, y1
) {
424 /** Calculate the anchor point for a box given extents and anchor mode
426 This function is the inverse of yuu.bottomLeft.
429 case "center": return [(x0
+ x1
) / 2, (y0
+ y1
) / 2];
430 case "top": return [(x0
+ x1
) / 2, y1
];
431 case "bottom": return [(x0
+ x1
) / 2, y0
];
432 case "left": return [x0
, (y0
+ y1
) / 2];
433 case "right": return [x1
, (y0
+ y1
) / 2];
435 case "topleft": return [x0
, y1
];
436 case "topright": return [x1
, y1
];
437 case "bottomleft": return [x0
, y0
];
438 case "bottomright": return [x0
, y0
];
439 default: return [anchor
[0], anchor
[1]];
443 yuu
.bottomLeft = function (anchor
, x
, y
, w
, h
) {
444 /** Calculate the bottom-left for a box given size and anchor mode
446 This function is the inverse of yuu.anchorPoint.
449 case "center": return [x
- w
/ 2, y
- h
/ 2];
450 case "top": return [x
- w
/ 2, y
- h
];
451 case "bottom": return [x
- w
/ 2, y
];
452 case "left": return [x
, y
- h
/ 2];
453 case "right": return [x
- w
, y
- h
/ 2];
455 case "topleft": return [x
, y
- h
];
456 case "topright": return [x
- w
, y
- h
];
457 case "bottomleft": return [x
, y
];
458 case "bottomright": return [x
- w
, y
];
459 default: return [anchor
[0], anchor
[1]];
463 yuu
.lerp = function (a
, b
, p
) {
464 return (a
!== null && a
!== undefined && a
.lerp
)
465 ? a
.lerp(b
, p
) : (b
!== null && b
!== undefined && b
.lerp
)
466 ? b
.lerp(a
, 1 - p
) : p
< 0.5 ? a
: b
;
469 yuu
.bilerp = function (x0y0
, x1y0
, x0y1
, x1y1
, px
, py
) {
470 /** Bilinearly interpolate between four values in two dimensions */
471 return yuu
.lerp(yuu
.lerp(x0y0
, x1y0
, px
), yuu
.lerp(x0y1
, x1y1
, px
), py
);
474 function resolvePropertyPath (object
, path
) {
475 /** Look up a full property path
477 If a null is encountered in the path, this function returns
478 null. If undefined is encountered or a property is missing, it
481 var parts
= path
.replace(/\[(\w+)\]/g, '.$1').split('.');
483 i
< parts
.length
&& object
!== undefined && object
!== null;
485 object
= object
[parts
[i
]];
491 /** Somewhat like Python's random.Random.
493 Passed a function that returns a uniform random variable in
494 [0, 1) it can do other useful randomization algorithms.
496 Its methods are implemented straightforwardly rather than
497 rigorously - this means they may not behave correctly in
498 common edge cases like precision loss.
500 constructor: function (generator
) {
501 this.random
= generator
|| Math
.random
;
502 this._spareGauss
= null;
505 choice: function (seq
) {
506 /** Return a random element from the provided array. */
507 return seq
[this.randrange(0, seq
.length
)];
512 /** Return a uniform random integer in [0, a). */
513 return (this.random() * a
) | 0;
517 /** Return a uniform random integer in [a, b). */
520 return a
+ ((this.random() * (b
- a
)) | 0);
523 function (a
, b
, step
) {
524 /** Return a uniform random number in [a, b).
526 The number is constrained to values of a + i * step
527 where i is a non-negative integer.
529 var i
= Math
.ceil((b
- a
) / step
);
530 return a
+ this.randrange(i
) * step
;
536 /** Return a uniform random variable in [0, a). */
537 return a
* this.random();
540 /** Return a uniform random variable in [a, b). */
541 return a
+ (b
- a
) * this.random();
545 gauss: function (mean
, sigma
) {
546 var u
= this._spareGauss
, v
, s
;
547 this._spareGauss
= null;
551 u
= this.uniform(-1, 1);
552 v
= this.uniform(-1, 1);
554 } while (s
>= 1.0 || s
=== 0.0);
555 var t
= Math
.sqrt(-2.0 * Math
.log(s
) / s
);
556 this._spareGauss
= v
* t
;
559 return mean
+ sigma
* u
;
563 /** Return true the given percent of the time (default 50%). */
564 function () { return this.random() < 0.5; },
565 function (a
) { return this.random() < a
; }
568 randsign: function (v
) {
569 return this.randbool() ? v
: -v
;
572 shuffle: function (seq
) {
573 for (var i
= seq
.length
- 1; i
> 0; --i
) {
574 var index
= this.randrange(i
+ 1);
582 discard: function (z
) {
589 yuu
.createLCG
= yf
.argcd(
590 /** Linear congruential random generator
592 This returns a function that generates numbers [0, 1) as
593 with Math.random. You can also read or assign the `state`
594 attribute to set the internal state.
596 function () { return yuu
.createLCG(Math
.random() * 2147483647); },
598 var state
= seed
| 0;
599 return function generator () {
600 state
= (state
* 1664525 + 1013904223) % 4294967296;
601 return state
/ 4294967296;
606 yuu
.random
= new yuu
.Random();
608 function defaultKey (args
) {
609 // Cache things that can be constructed with one string.
610 return args
.length
=== 1 && yf
.isString(args
[0]) ? args
[0] : null;
613 yuu
.Caching = function (Type
, cacheKey
) {
615 var k
= ctor
._cacheKey(arguments
);
616 var o
= k
&& ctor
._cache
[k
];
618 o
= ctor
._cache
[k
] = yf
.construct(ctor
.Uncached
, arguments
);
621 ctor
._cacheKey
= cacheKey
|| defaultKey
;
623 ctor
.Uncached
= Type
;
627 yuu
.transpose2d = function (a
) {
628 for (var x
= 0; x
< a
.length
; ++x
) {
629 for (var y
= 0; y
< x
; ++y
) {
637 yuu
.normalizeRadians = function (theta
) {
639 return (theta
+ 3 * PI
) % (2 * PI
) - PI
;
642 yuu
.radians = function (v
) {
643 return v
* (Math
.PI
/ 180.0);
646 yuu
.degrees = function (v
) {
647 return v
* (180.0 / Math
.PI
);
650 var SHORT
= /(\/|^)@(.+)$/;
651 yuu
.resourcePath = function (path
, category
, ext
) {
653 if ((match
= path
.match(SHORT
))) {
654 path
= path
.replace(/^yuu\/@/, yuu
.PATH
+ "@")
655 .replace(SHORT
, "$1data/" + category
+ "/$2");
656 if (match
[2].indexOf(".") === -1)
662 yuu
.ready = function (resources
, result
) {
663 return Promise
.all(yf
.filter(null, yf
.pluck("ready", resources
)))
667 yuu
.openURL = function (url
) {
668 if (gui
&& gui
.Shell
)
669 gui
.Shell
.openExternal(url
);
674 function crossPlatformFilename (basename
) {
676 // Replace D/M/Y with D-M-Y, and H:M:S with H.M.S.
677 .replace(/\//g, "-").replace(/:/g
, ".")
678 // Replace all other problematic characters with _.
679 .replace(/["<>*?|\\]/g, "_");
682 yuu
.downloadURL = function (url
, suggestedName
) {
683 var regex
= /^data:[^;+]+;base64,(.*)$/;
684 var matches
= url
.match(regex
);
685 suggestedName
= crossPlatformFilename(suggestedName
);
687 var data
= matches
[1];
688 var buffer
= new Buffer(data
, 'base64');
689 var HOME
= process
.env
.HOME
690 || process
.env
.HOMEPATH
691 || process
.env
.USERPROFILE
;
692 var filename
= HOME
+ "/" + suggestedName
;
693 console
.log("Saving to", filename
);
694 fs
.writeFileSync(filename
, buffer
);
696 var link
= document
.createElement('a');
697 link
.style
.display
= "none";
699 link
.download
= suggestedName
;
700 // Firefox (as of 28) won't download from a link not rooted in
701 // the document; so, root it and then remove it when done.
702 document
.body
.appendChild(link
);
704 document
.body
.removeChild(link
);
709 constructor: yf
.argcd(
710 function () { this.constructor(0, 0, 0, 0); },
711 function (w
, h
) { this.constructor(0, 0, w
, h
); },
712 function (x0
, y0
, x1
, y1
) {
721 get: function () { return this.x1
- this.x0
; },
722 set: function (w
) { this.x1
= this.x0
+ w
; }
726 get: function () { return this.y1
- this.y0
; },
727 set: function (h
) { this.y1
= this.y0
+ h
; }
730 size
: { swizzle
: "wh" },
733 function (p
) { return this.contains(p
.x
, p
.y
); },
735 return x
>= this.x0
&& x
< this.x1
736 && y
>= this.y0
&& y
< this.y1
;
740 matchAspectRatio: function (outer
) {
741 var matched
= new this.constructor(
742 this.x0
, this.y0
, this.x1
, this.y1
);
743 var aRatio
= matched
.w
/ matched
.h
;
744 var bRatio
= outer
.w
/ outer
.h
;
745 if (aRatio
> bRatio
) {
746 // too wide, must be taller
747 var h
= matched
.w
/ bRatio
;
748 var dh
= h
- matched
.h
;
749 matched
.y0
-= dh
/ 2;
750 matched
.y1
+= dh
/ 2;
752 // too tall, must be wider
753 var w
= matched
.h
* bRatio
;
754 var dw
= w
- matched
.w
;
755 matched
.x0
-= dw
/ 2;
756 matched
.x1
+= dw
/ 2;
761 alignedInside: function (outer
, alignment
) {
769 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
773 x0
= outer
.x0
- this.w
;
778 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
781 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
782 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
785 x0
= outer
.x1
- this.w
;
786 y0
= outer
.x0
+ (outer
.h
- this.h
) / 2;
790 y0
= outer
.y1
- this.h
;
793 x0
= outer
.x0
+ (outer
.w
- this.w
) / 2;
794 y0
= outer
.y1
- this.h
;
797 x0
= outer
.x1
- this.w
;
798 y0
= outer
.y1
- this.h
;
801 return new this.constructor(x0
, y0
, x0
+ this.w
, y0
+ this.h
);
805 function splitPathExtension (path
) {
806 var dot
= path
.lastIndexOf(".");
807 if (dot
<= 0) return [path
, ""];
809 var dir
= path
.lastIndexOf("/");
810 if (dot
< dir
) return [path
, ""];
812 return [path
.substring(0, dot
), path
.substring(dot
)];
814 yuu
.splitPathExtension
= splitPathExtension
;
817 yT
.defineProperty(String
.prototype, "lerp", function (b
, p
) {
819 // Never numericLerp - if that's desired force Numbers.
820 // Be more conservative than stringLerp since this runs
821 // often and the diff can't be easily hoisted.
822 return this.length
* b
.length
> 256
823 ? stringLerp
.fastLerp(this, b
, p
)
824 : stringLerp
.diffLerp(this, b
, p
);
828 yT
.defineProperties(Number
.prototype, {
829 lerp: function (b
, p
) { return this + (b
- this) * p
; }
832 yT
.defineProperties(Array
.prototype, {
833 lerp: function (b
, p
) {
834 var length
= Math
.round(this.length
.lerp(b
.length
, p
));
835 var c
= new this.constructor(length
);
836 for (var i
= 0; i
< length
; ++i
) {
837 if (i
>= this.length
)
839 else if (i
>= b
.length
)
842 c
[i
] = this[i
].lerp(b
[i
], p
);
848 /** Typed array extensions
850 https://www.khronos.org/registry/typedarray/specs/1.0/
851 BUT: Read on for fun times in browser land~
853 Ideally we could just set these once on ArrayBufferView, but
854 the typed array specification doesn't require that such a
855 constructor actually exist. And in Firefox (18), it doesn't.
857 More infurating, in Safari (7.0.3) Int8Array etc. are not
858 functions so this needs to be added to the prototype
859 directly. This is a violation of the specification which
860 requires such constructors, and ECMA which requires
861 constructors be functions, and common decency.
864 [ Float32Array
, Float64Array
, Int8Array
, Uint8Array
,
865 Int16Array
, Uint16Array
, Int32Array
, Uint32Array
866 ].forEach(function (A
) {
867 yT
.defineProperties(A
.prototype, {
869 /** Like Array's slice, but for typed arrays */
870 function () { return new this.constructor(this); },
872 return new this.constructor(this.subarray(begin
));
874 function (begin
, end
) {
875 return new this.constructor(this.subarray(begin
, end
));
879 fill
: Array
.prototype.fill
,
880 reverse
: Array
.prototype.reverse
,
882 lerp: function (b
, p
) {
887 var c
= new this.constructor(this.length
);
888 for (var i
= 0; i
< this.length
; ++i
)
889 c
[i
] = this[i
] + (b
[i
] - this[i
]) * p
;
895 }).call(typeof exports
=== "undefined" ? this : exports
,
896 typeof exports
=== "undefined" ? (this.yuu
= {}) : exports
);